refactor(board): atomic delete + query limit + composable cleanup
Delete uses findOneAndDelete with author match (no TOCTOU window); existence check only runs on miss to distinguish 403 vs 404. Posts list capped at 200. Drop unused resolveTagChannel and refreshParams; route slack URL building through the composable's slackUrl helper.
This commit is contained in:
parent
d1a1484daf
commit
28040f44f4
7 changed files with 30 additions and 54 deletions
|
|
@ -86,6 +86,8 @@ const props = defineProps({
|
|||
|
||||
defineEmits(['edit', 'delete', 'confirm-delete', 'cancel-delete'])
|
||||
|
||||
const { slackUrl } = useBoardChannels()
|
||||
|
||||
const capitalizeAvatar = (str) => {
|
||||
if (str.toLowerCase() === 'wtf') return 'WTF'
|
||||
return str
|
||||
|
|
@ -148,7 +150,7 @@ const slackLinks = computed(() => {
|
|||
.map((c) => ({
|
||||
id: c.slackChannelId,
|
||||
name: c.slackChannelName || c.name || c.slackChannelId,
|
||||
url: `https://gammaspace.slack.com/archives/${c.slackChannelId}`,
|
||||
url: slackUrl(c.slackChannelId),
|
||||
}))
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
/**
|
||||
* Board Channels Composable
|
||||
* Shared state + helpers for mapping board tags to Slack channels.
|
||||
*/
|
||||
export function useBoardChannels() {
|
||||
const channels = useState('board.channels', () => [])
|
||||
|
||||
|
|
@ -11,15 +7,6 @@ export function useBoardChannels() {
|
|||
return channels.value
|
||||
}
|
||||
|
||||
function resolveTagChannel(tagSlugs = []) {
|
||||
if (!tagSlugs?.length) return null
|
||||
return (
|
||||
channels.value.find((channel) =>
|
||||
(channel.tagSlugs || []).some((slug) => tagSlugs.includes(slug))
|
||||
) || null
|
||||
)
|
||||
}
|
||||
|
||||
function slackUrl(channelId) {
|
||||
return `https://gammaspace.slack.com/archives/${channelId}`
|
||||
}
|
||||
|
|
@ -27,7 +14,6 @@ export function useBoardChannels() {
|
|||
return {
|
||||
channels: readonly(channels),
|
||||
fetchChannels,
|
||||
resolveTagChannel,
|
||||
slackUrl,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
/**
|
||||
* Board Posts Composable
|
||||
* Shared state + CRUD for board posts.
|
||||
*/
|
||||
export function useBoardPosts() {
|
||||
const posts = useState('board.posts', () => [])
|
||||
const loading = useState('board.loading', () => false)
|
||||
|
|
@ -17,29 +13,29 @@ export function useBoardPosts() {
|
|||
}
|
||||
}
|
||||
|
||||
async function createPost(body, refreshParams = {}) {
|
||||
async function createPost(body) {
|
||||
const created = await $fetch('/api/board/posts', {
|
||||
method: 'POST',
|
||||
body,
|
||||
})
|
||||
await fetchPosts(refreshParams)
|
||||
await fetchPosts()
|
||||
return created
|
||||
}
|
||||
|
||||
async function updatePost(id, body, refreshParams = {}) {
|
||||
async function updatePost(id, body) {
|
||||
const updated = await $fetch(`/api/board/posts/${id}`, {
|
||||
method: 'PATCH',
|
||||
body,
|
||||
})
|
||||
await fetchPosts(refreshParams)
|
||||
await fetchPosts()
|
||||
return updated
|
||||
}
|
||||
|
||||
async function deletePost(id, refreshParams = {}) {
|
||||
async function deletePost(id) {
|
||||
const result = await $fetch(`/api/board/posts/${id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
await fetchPosts(refreshParams)
|
||||
await fetchPosts()
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
<template>
|
||||
<PageShell title="Bulletin Board" :subtitle="pageSubtitle">
|
||||
<!-- Action bar -->
|
||||
<div class="action-bar">
|
||||
<button type="button" class="new-post-btn" @click="openNewForm">
|
||||
+ New Post
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tags Drawer Toggle -->
|
||||
<div v-if="cooperativeTags.length > 0" class="tags-drawer-toggle">
|
||||
<button type="button" class="drawer-btn" @click="showTagsDrawer = !showTagsDrawer">
|
||||
Tags...
|
||||
|
|
@ -15,7 +13,6 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tags Drawer -->
|
||||
<div v-if="showTagsDrawer && cooperativeTags.length > 0" class="tags-drawer">
|
||||
<div class="skills-bar">
|
||||
<span class="tag-label">Filter:</span>
|
||||
|
|
@ -40,7 +37,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inline form -->
|
||||
<div v-if="showForm" class="form-wrapper">
|
||||
<BoardPostForm
|
||||
:post="editingPost"
|
||||
|
|
@ -50,7 +46,6 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<ClientOnly>
|
||||
<div v-if="loading" class="loading-state">
|
||||
<p>Loading board...</p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue