Compare commits
14 Commits
1e2503ef44
...
master
Author | SHA1 | Date | |
---|---|---|---|
3f4c542265 | |||
3d4dae9515 | |||
88d47ef535 | |||
da32c3bdfa | |||
9778a49c64 | |||
79e0e5651a | |||
a7e6488e72 | |||
9c74f41022 | |||
98711948b6 | |||
f60d5f2114 | |||
c8e91ba949 | |||
78af91e971 | |||
af219e69bb | |||
f3360b6041 |
32
README.md
Normal file
32
README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Paste
|
||||
|
||||
A simple pastebin service. Running at [paste.fascinated.cc](https://paste.fascinated.cc).
|
||||
|
||||
## Javascript Utility
|
||||
|
||||
```js
|
||||
/**
|
||||
* Uploads a paste to paste.fascinated.cc
|
||||
*
|
||||
* @param content the content of the paste
|
||||
* @returns the paste key and the URL
|
||||
*/
|
||||
async function uploadPaste(content: string) {
|
||||
const response = await fetch("https://paste.fascinated.cc/api/upload", {
|
||||
method: "POST",
|
||||
body: content,
|
||||
});
|
||||
const json = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(json.message);
|
||||
}
|
||||
|
||||
return {
|
||||
...json,
|
||||
url: `https://paste.fascinated.cc/${json.key}`,
|
||||
};
|
||||
}
|
||||
|
||||
console.log(await uploadPaste("Hello, World!"));
|
||||
```
|
2
pom.xml
2
pom.xml
@ -87,7 +87,7 @@
|
||||
<dependency>
|
||||
<groupId>io.sentry</groupId>
|
||||
<artifactId>sentry-spring-boot-starter-jakarta</artifactId>
|
||||
<version>7.9.0</version>
|
||||
<version>7.10.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -4,10 +4,18 @@ import lombok.NonNull;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class Config {
|
||||
@Configuration @EnableWebMvc
|
||||
public class Config implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry
|
||||
.addResourceHandler("/static/**")
|
||||
.addResourceLocations("classpath:/static/");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebMvcConfigurer configureCors() {
|
||||
|
@ -4,11 +4,15 @@ import cc.fascinated.backend.exception.impl.ResourceNotFoundException;
|
||||
import cc.fascinated.backend.model.Paste;
|
||||
import cc.fascinated.backend.service.PasteService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.awt.datatransfer.MimeTypeParseException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
@ -29,13 +33,20 @@ public class IndexController {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@GetMapping(value = "/{id}")
|
||||
/**
|
||||
* This is to allow for Hastebin compatibility.
|
||||
*/
|
||||
@PostMapping(value = "/documents")
|
||||
public ResponseEntity<?> uploadPaste(@RequestBody String content, @RequestHeader(value = HttpHeaders.CONTENT_TYPE, required = false) String contentType) {
|
||||
String id = pasteService.createPaste(content, contentType);
|
||||
return ResponseEntity.ok(Map.of("key", id));
|
||||
}
|
||||
|
||||
|
||||
@GetMapping(value = "/{id}", produces = MimeTypeUtils.TEXT_HTML_VALUE)
|
||||
public String paste(@PathVariable String id, Model model) {
|
||||
try {
|
||||
Paste paste = pasteService.getPaste(id);
|
||||
if (paste == null) { // If the paste does not exist, redirect to the home page
|
||||
return "redirect:/";
|
||||
}
|
||||
model.addAttribute("paste", paste);
|
||||
model.addAttribute("title", "Paste - " + paste.getId());
|
||||
model.addAttribute("rawUrl", "/raw/" + paste.getId());
|
||||
@ -45,7 +56,7 @@ public class IndexController {
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping(value = "/raw/{id}")
|
||||
@GetMapping(value = "/raw/{id}", produces = MimeTypeUtils.TEXT_HTML_VALUE)
|
||||
public String pasteRaw(@PathVariable String id, Model model) {
|
||||
try {
|
||||
Paste paste = pasteService.getPaste(id);
|
||||
|
@ -3,6 +3,7 @@ package cc.fascinated.backend.controller;
|
||||
import cc.fascinated.backend.model.Paste;
|
||||
import cc.fascinated.backend.service.PasteService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -20,9 +21,9 @@ public class PasteController {
|
||||
}
|
||||
|
||||
@PostMapping(value = "/upload")
|
||||
public ResponseEntity<?> uploadPaste(@RequestBody String content) {
|
||||
String id = pasteService.createPaste(content);
|
||||
return ResponseEntity.ok(Map.of("id", id));
|
||||
public ResponseEntity<?> uploadPaste(@RequestBody String content, @RequestHeader(value = HttpHeaders.CONTENT_TYPE, required = false) String contentType) {
|
||||
String id = pasteService.createPaste(content, contentType);
|
||||
return ResponseEntity.ok(Map.of("key", id));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/paste/{id}")
|
||||
|
@ -44,7 +44,7 @@ public class PasteService {
|
||||
* @param content The content of the paste.
|
||||
* @return The id of the paste.
|
||||
*/
|
||||
public String createPaste(String content) {
|
||||
public String createPaste(String content, String contentType) {
|
||||
int length = content.length();
|
||||
long before = System.currentTimeMillis();
|
||||
log.info("Creating a new paste. (characters: {})", length);
|
||||
@ -52,12 +52,19 @@ public class PasteService {
|
||||
// Check if the content is too large.
|
||||
if (length > uploadSizeLimit && uploadSizeLimit != -1) {
|
||||
log.info("Paste didn't meet the size requirements. (characters: {})", length);
|
||||
throw new BadRequestException("The paste content is too large, the limit is " + uploadSizeLimit + " characters");
|
||||
throw new BadRequestException("The paste content is too large, the limit is " + uploadSizeLimit + " characters, not uploading...");
|
||||
}
|
||||
|
||||
// Ensure the paste content type is valid.
|
||||
if (contentType != null && (contentType.contains("image") || contentType.contains("video") || contentType.contains("audio"))) {
|
||||
log.info("Paste content type is not supported. (content type: {})", contentType);
|
||||
throw new BadRequestException("The paste content type is not supported, not uploading...");
|
||||
}
|
||||
|
||||
// Ensure the paste content does not contain a file header.
|
||||
if (FileHeaderChecker.containsFileHeader(content)) {
|
||||
throw new BadRequestException("The paste content contains a file header");
|
||||
log.info("Paste content contains a file header, not uploading...");
|
||||
throw new BadRequestException("The paste content contains a file header, not uploading...");
|
||||
}
|
||||
|
||||
// Save the paste to the database.
|
||||
@ -77,6 +84,7 @@ public class PasteService {
|
||||
* @return The content of the paste.
|
||||
*/
|
||||
public Paste getPaste(String id) {
|
||||
id = id.contains(".") ? id.split("\\.")[0] : id; // Remove file extensions (if any)
|
||||
log.info("Getting paste with the id '{}'", id);
|
||||
long before = System.currentTimeMillis();
|
||||
Optional<Paste> paste = pasteRepository.findById(id);
|
||||
|
49
src/main/resources/static/assets/script.js
Normal file
49
src/main/resources/static/assets/script.js
Normal file
@ -0,0 +1,49 @@
|
||||
// Handle custom key binds behavior
|
||||
document.addEventListener("keydown", function (event) {
|
||||
// Upload the paste when Ctrl + Enter is pressed
|
||||
if (event.ctrlKey && event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
upload();
|
||||
}
|
||||
});
|
||||
|
||||
// Upload the paste when the paste button is clicked
|
||||
document.getElementById("paste-button").addEventListener("click", () => upload());
|
||||
|
||||
// Upload the paste to the server
|
||||
const upload = async () => {
|
||||
var pasteInput = document.getElementById("paste-input");
|
||||
var paste = pasteInput.value;
|
||||
|
||||
if (!paste || paste.trim() === "") {
|
||||
pasteInput.focus();
|
||||
toast("Please enter a paste to upload.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Uploading paste...");
|
||||
try {
|
||||
const response = await fetch("/api/upload", {
|
||||
method: "POST",
|
||||
body: paste,
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message);
|
||||
}
|
||||
|
||||
window.location.href = "/" + data.key;
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
toast(`${error.message || "An error occurred while uploading the paste."}`);
|
||||
}
|
||||
};
|
||||
|
||||
const toast = (message, duration = 3000) => {
|
||||
Toastify({
|
||||
text: message,
|
||||
duration: duration,
|
||||
gravity: "bottom",
|
||||
position: "right"
|
||||
}).showToast();
|
||||
};
|
62
src/main/resources/static/tailwindcss.js
Normal file
62
src/main/resources/static/tailwindcss.js
Normal file
File diff suppressed because one or more lines are too long
@ -8,7 +8,11 @@
|
||||
<title>Paste</title>
|
||||
|
||||
<!-- TailwindCSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="/static/tailwindcss.js"></script>
|
||||
|
||||
<!-- Toastify -->
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css">
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
|
||||
</head>
|
||||
<body class="bg-neutral-900 text-white">
|
||||
<div class="p-3 h-screen w-screen relative">
|
||||
@ -29,39 +33,5 @@
|
||||
</body>
|
||||
|
||||
<!-- Paste Script -->
|
||||
<script>
|
||||
// Handle custom key binds behavior
|
||||
document.addEventListener('keydown', function(event) {
|
||||
// Upload the paste when Ctrl + Enter is pressed
|
||||
if (event.ctrlKey && event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
upload();
|
||||
}
|
||||
});
|
||||
|
||||
// Upload the paste when the paste button is clicked
|
||||
document.getElementById('paste-button').addEventListener('click', () => upload());
|
||||
|
||||
// Upload the paste to the server
|
||||
const upload = () => {
|
||||
var pasteInput = document.getElementById('paste-input');
|
||||
var paste = pasteInput.value;
|
||||
|
||||
if (!paste || paste.trim() === ''){
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/upload', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/plain'
|
||||
},
|
||||
body: paste
|
||||
}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(data) {
|
||||
window.location.href = '/' + data.id;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<script src="/static/assets/script.js"></script>
|
||||
</html>
|
@ -12,7 +12,7 @@
|
||||
<meta content="Click to view the Paste." property="og:description" />
|
||||
|
||||
<!-- TailwindCSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="/static/tailwindcss.js"></script>
|
||||
|
||||
<!-- Highlight.js -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
|
||||
|
Reference in New Issue
Block a user