Skip to content
Draft
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
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ services:
- ./packages/web-app-importer/dist:/web/apps/importer
- ./packages/web-app-json-viewer/dist:/web/apps/json-viewer
- ./packages/web-app-maps/dist:/web/apps/maps
- ./packages/web-app-pastebin/dist:/web/apps/pastebin
- ./packages/web-app-progress-bars/dist:/web/apps/progress-bars
- ./packages/web-app-unzip/dist:/web/apps/unzip
depends_on:
Expand Down
11 changes: 11 additions & 0 deletions packages/web-app-pastebin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# web-app-pastebin

This is an application for creating and sharing text snippets and files via public links. Similar to traditional pastebin services, but integrated with OpenCloud's storage and sharing capabilities.

## Features

- **Quick Text Sharing**: Create multiple text snippets at once via a simple interface
- **Automatic Organization**: Files are automatically organized in `.space/pastebin/` with timestamp-based folders
- **Public Link Generation**: Automatically generates shareable links
- **Multiple File Support**: Display multiple files in one pastebin
- **View Mode**: Dedicated view for displaying pastebin contents
10 changes: 10 additions & 0 deletions packages/web-app-pastebin/l10n/.tx/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[main]
host = https://www.transifex.com

[o:opencloud-eu:p:opencloud-eu:r:web-extensions-pastebin]
file_filter = locale/<lang>/app.po
minimum_perc = 0
resource_name = web-extensions-pastebin
source_file = template.pot
source_lang = en
type = PO
1 change: 1 addition & 0 deletions packages/web-app-pastebin/l10n/translations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
20 changes: 20 additions & 0 deletions packages/web-app-pastebin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "pastebin",
"version": "1.0.0",
"private": true,
"description": "OpenCloud Web Pastebin",
"license": "AGPL-3.0",
"type": "module",
"scripts": {
"build": "pnpm vite build",
"build:w": "pnpm vite build --watch --mode development",
"check:types": "vue-tsc --noEmit",
"test:unit": "NODE_OPTIONS=--unhandled-rejections=throw vitest"
},
"devDependencies": {
"@opencloud-eu/web-client": "^3.0.0",
"@opencloud-eu/web-pkg": "^3.0.0",
"vue": "^3.4.21",
"vue3-gettext": "^2.4.0"
}
}
195 changes: 195 additions & 0 deletions packages/web-app-pastebin/src/Create.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
<template>
<div class="create-pastebin">
<h1>Create Pastebin</h1>
<oc-textarea
v-model="currentContent"
class="pastebin-textarea"
placeholder="Enter your content here..."
/>

<div class="button-container">
<oc-button appearance="filled" size="medium" @click.stop.prevent="save()">
Save Pastebin
</oc-button>
</div>
</div>
</template>

<script setup lang="ts">
import { ref, unref } from 'vue'
import {
useClientService,
useMessages,
useSpacesStore,
useSharesStore
} from '@opencloud-eu/web-pkg'
import { WebDAV } from '@opencloud-eu/web-client/webdav'
import { useLinkTypes } from '@opencloud-eu/web-pkg'
import { useClipboard, useRouter } from '@opencloud-eu/web-pkg'

const currentContent = ref<string>('')
const { showMessage, showErrorMessage } = useMessages()
const clientService = useClientService()
const spacesStore = useSpacesStore()
const { addLink } = useSharesStore()
const { defaultLinkType } = useLinkTypes()
const { copyToClipboard } = useClipboard()
const router = useRouter()

const save = async () => {
if (!spacesStore.personalSpace) {
showErrorMessage({
title: 'No personal space available',
errors: ['Cannot create file without access to personal space']
})
return
}

if (!currentContent.value.trim()) {
showErrorMessage({
title: 'Empty content',
errors: ['Please enter some content before saving']
})
return
}

try {
// Generate timestamp for both folder and file
const now = new Date()
const timestamp = now.toISOString().replace(/[:.]/g, '-')

// Create folder structure step by step: .space -> pastebin -> exact-timestamp
const webdav = clientService.webdav as WebDAV

// Create .space folder first
try {
await webdav.createFolder(spacesStore.personalSpace, { path: '/.space' })
} catch (error) {
// Folder may already exist, ignore error
console.log('.space folder creation (may already exist):', error)
}

// Create pastebin folder
try {
await webdav.createFolder(spacesStore.personalSpace, { path: '/.space/pastebin' })
} catch (error) {
// Folder may already exist, ignore error
console.log('pastebin folder creation (may already exist):', error)
}

// Create timestamp folder
const folderPath = `/.space/pastebin/${timestamp}`
try {
await webdav.createFolder(spacesStore.personalSpace, { path: folderPath })
} catch (error) {
// Folder may already exist, ignore error
console.log('timestamp folder creation (may already exist):', error)
}

// Generate filename and full file path
const fileName = `pastebin-${timestamp}.txt`
const filePath = `${folderPath}/${fileName}`

const resource = await (clientService.webdav as WebDAV).putFileContents(
spacesStore.personalSpace,
{
path: filePath,
content: currentContent.value
}
)

// Get the folder resource to create a public link for it
const folderResource = await (clientService.webdav as WebDAV).getFileInfo(
spacesStore.personalSpace,
{ path: folderPath }
)

// Create a public link for the folder with password protection
try {
const linkShare = await addLink({
clientService,
space: spacesStore.personalSpace,
resource: folderResource,
options: {
'@libre.graph.quickLink': false,
displayName: `Pastebin Link ${timestamp}`,
type: unref(defaultLinkType),
password: 'Foobar!64'
}
})

// Copy link and password to clipboard
const clipboardText = `${linkShare.webUrl}\nPassword: Foobar!64`
await copyToClipboard(clipboardText)

showMessage({
title: `Pastebin "${resource.name}" was created and link copied to clipboard!`,
desc: `Public link: ${linkShare.webUrl} (Password: Foobar!64)`
})
} catch (linkError) {
console.error('Failed to create public link:', linkError)
showMessage({
title: `Pastebin "${resource.name}" was created successfully`
})
showErrorMessage({
title: 'Failed to create public link',
errors: [linkError]
})
}

// Clear the content after successful save
currentContent.value = ''

// Redirect to the show route with the folder path
const driveAliasAndItem = spacesStore.personalSpace.getDriveAliasAndItem({ path: folderPath })
console.log('Redirecting to show route with:', driveAliasAndItem)
await router.push(`/pastebin/show/${driveAliasAndItem}`)
console.log('Pastebin file created:', resource)
return resource
} catch (error) {
console.error('Failed to create pastebin file:', error)
showErrorMessage({
title: 'Failed to create file',
errors: [error]
})
throw error
}
}
</script>

<style scoped>
.create-pastebin {
display: flex;
flex-direction: column;
height: 100vh;
padding: 1rem;
}

.pastebin-textarea {
width: 80%;
height: 60%;
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 14px;
resize: both;
outline: none;
}

.pastebin-textarea:focus {
border-color: #007acc;
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
}

h1 {
margin-bottom: 1rem;
color: #333;
}

.button-container {
margin-top: 1.5rem;
display: flex;
justify-content: flex-start;
}
</style>
Loading