Skip to content

Commit 25a0fd1

Browse files
authored
Merge pull request #42 from johnwmail/feature/upload-auth
Feature/upload auth
2 parents dabcbe4 + 68d0168 commit 25a0fd1

File tree

6 files changed

+91
-89
lines changed

6 files changed

+91
-89
lines changed

Documents/LAMBDA.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Create an IAM role with the following permissions:
6363
"s3:GetObject",
6464
"s3:PutObject",
6565
"s3:DeleteObject",
66-
"s3:HeadObject"
66+
"s3:ListBucket"
6767
],
6868
"Resource": [
6969
"arn:aws:s3:::YOUR_BUCKET_NAME",
@@ -79,6 +79,7 @@ Create an IAM role with the following permissions:
7979
- `s3:GetObject` - Read paste content and metadata
8080
- `s3:PutObject` - Create new pastes
8181
- `s3:DeleteObject` - Clean up expired pastes
82+
- `s3:ListBucket` - List objects for slug uniqueness checks
8283

8384
### GitHub Actions Permissions
8485

@@ -486,4 +487,4 @@ aws ce get-cost-and-usage \
486487
- [AWS Lambda Documentation](https://docs.aws.amazon.com/lambda/)
487488
- [Amazon S3 Documentation](https://docs.aws.amazon.com/s3/)
488489
- [CloudWatch Logs Documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/)
489-
- [nclip GitHub Repository](https://github.com/johnwmail/nclip)
490+
- [nclip GitHub Repository](https://github.com/johnwmail/nclip)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ aws lambda create-function \
183183
"s3:GetObject",
184184
"s3:PutObject",
185185
"s3:DeleteObject",
186-
"s3:HeadObject"
186+
"s3:ListBucket"
187187
],
188188
"Resource": "*"
189189
}

static/index.html

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta charset="UTF-8">
66
<meta name="viewport" content="width=device-width, initial-scale=1.0">
77
<title>{{.Title}}</title>
8-
<link rel="stylesheet" href="/static/style.css">
8+
<link rel="stylesheet" href="/static/style.css?v={{.Version}}">
99
<link rel="preconnect" href="https://fonts.googleapis.com">
1010
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
1111
<link
@@ -44,14 +44,7 @@ <h2>
4444
<div class="form-group">
4545
<label for="text-content">Text Content</label>
4646
<textarea id="text-content" placeholder="Paste your text here..."></textarea>
47-
{{ if .UploadAuth }}
48-
<!-- API Key for uploads (server requires it) -->
49-
<div class="api-key-wrapper" style="margin-top:0.5rem;">
50-
<label for="api-key-input">API Key</label>
51-
<input type="text" id="api-key-input" placeholder="Paste API key for uploads"
52-
style="width:100%;" />
53-
</div>
54-
{{ end }}
47+
<!-- API key moved below file upload controls -->
5548
<div class="form-controls">
5649
<div class="checkbox-wrapper">
5750
<input type="checkbox" id="burn-text">
@@ -71,6 +64,7 @@ <h2>
7164
<div class="form-group">
7265
<label for="file-input">File Upload</label>
7366
<input type="file" id="file-input">
67+
<!-- API key unified below -->
7468
<div class="form-controls">
7569
<div class="checkbox-wrapper">
7670
<input type="checkbox" id="burn-file">
@@ -85,6 +79,12 @@ <h2>
8579
</button>
8680
</div>
8781
</div>
82+
{{ if .UploadAuth }}
83+
<div class="form-group api-key-row">
84+
<label for="api-key-input">API Key</label>
85+
<input type="text" id="api-key-input" placeholder="Paste API key for uploads" style="width:100%;" />
86+
</div>
87+
{{ end }}
8888
</div>
8989

9090
<div class="card result-section" id="result-section">

static/script.js

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,25 @@ document.addEventListener('DOMContentLoaded', function () {
2020
const usageExamplesContainer = document.querySelector('.code-examples');
2121
const initialUsageHTML = usageExamplesContainer ? usageExamplesContainer.innerHTML : '';
2222

23-
// API key input (optional)
23+
// API key inputs (optional)
2424
const apiKeyInput = document.getElementById('api-key-input');
25+
const apiKeyInputMobile = document.getElementById('api-key-input-mobile');
26+
27+
// Helper to get the API key value. Prefer mobile input if visible.
28+
function getApiKey() {
29+
try {
30+
if (apiKeyInputMobile) {
31+
// Check if mobile input is visible
32+
const style = window.getComputedStyle(apiKeyInputMobile);
33+
if (style && style.display !== 'none') {
34+
return apiKeyInputMobile.value.trim();
35+
}
36+
}
37+
} catch (e) {
38+
// ignore
39+
}
40+
return apiKeyInput ? apiKeyInput.value.trim() : '';
41+
}
2542

2643
// Text upload
2744
uploadTextBtn.addEventListener('click', function () {
@@ -41,10 +58,8 @@ document.addEventListener('DOMContentLoaded', function () {
4158
'Content-Type': 'text/plain',
4259
'Accept': 'application/json',
4360
};
44-
const key = apiKeyInput ? apiKeyInput.value.trim() : '';
45-
if (key) {
46-
headers['Authorization'] = 'Bearer ' + key;
47-
}
61+
const key = getApiKey();
62+
if (key) headers['Authorization'] = 'Bearer ' + key;
4863

4964
fetch(endpoint, {
5065
method: 'POST',
@@ -123,7 +138,7 @@ document.addEventListener('DOMContentLoaded', function () {
123138
uploadFileBtn.textContent = 'Uploading...';
124139

125140
const headers = { 'Accept': 'application/json' };
126-
const key = apiKeyInput ? apiKeyInput.value.trim() : '';
141+
const key = getApiKey();
127142
if (key) headers['Authorization'] = 'Bearer ' + key;
128143

129144
fetch(endpoint, {
@@ -206,48 +221,9 @@ document.addEventListener('DOMContentLoaded', function () {
206221
// Always reload the main page to ensure a pristine, server-rendered state
207222
event.preventDefault();
208223
window.location.assign('/');
209-
return;
210-
211-
// Legacy fallback (kept for reference, unreachable due to return above)
212-
// Hide result section
213-
resultSection.style.display = 'none';
214-
215-
// Show upload section again
216-
const uploadSection = document.querySelector('.upload-section');
217-
if (uploadSection) {
218-
uploadSection.style.display = 'block';
219-
}
220-
221-
// Reset all form fields
222-
textContent.value = '';
223-
fileInput.value = '';
224-
burnTextCheckbox.checked = false;
225-
burnFileCheckbox.checked = false;
226-
227-
// Remove any file selection
228-
if (fileInput) {
229-
fileInput.value = '';
230-
}
231-
232-
// Restore original usage examples exactly as initial render
233-
if (usageExamplesContainer) {
234-
usageExamplesContainer.innerHTML = initialUsageHTML;
235-
}
236-
237-
// Show all usage examples (in case any were hidden)
238-
const examples = document.querySelectorAll('.example');
239-
examples.forEach(e => e.style.display = 'block');
240-
241-
// Show the usage section if it was hidden
242-
if (usageSection) usageSection.style.display = 'block';
243-
244-
// Scroll to top for a clean experience
245-
window.scrollTo({ top: 0, behavior: 'smooth' });
246-
247-
// Focus on the text area
248-
textContent.focus();
249224
});
250225

226+
251227
// Update usage examples to show how to read the created paste
252228
function updateUsageExamples(url, slug) {
253229
const baseUrl = url.replace('/' + slug, '');
@@ -269,9 +245,6 @@ document.addEventListener('DOMContentLoaded', function () {
269245
}
270246
}
271247

272-
// Restore original usage examples for new paste
273-
function restoreOriginalUsageExamples() { /* no-op: handled by initialUsageHTML restore */ }
274-
275248
// Keyboard shortcuts
276249
document.addEventListener('keydown', function (e) {
277250
// Ctrl+Enter to upload text

static/style.css

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ header {
5858
text-align: center;
5959
margin-bottom: 1.5rem;
6060
flex-shrink: 0;
61+
display: flex;
62+
flex-direction: column;
63+
align-items: center;
6164
}
6265

6366
header h1 {
@@ -92,25 +95,10 @@ body.main-page main {
9295
background: var(--surface-elevated);
9396
border: 1px solid var(--border);
9497
border-radius: var(--radius);
95-
padding: 1.5rem;
96-
flex-shrink: 0;
97-
box-shadow: var(--shadow);
98-
transition: box-shadow 0.2s ease;
99-
}
100-
101-
.card:hover {
98+
padding: 1rem;
10299
box-shadow: var(--shadow-lg);
103100
}
104101

105-
.card h2 {
106-
font-size: 1.125rem;
107-
font-weight: 600;
108-
color: var(--text-primary);
109-
margin-bottom: 1rem;
110-
display: flex;
111-
align-items: center;
112-
gap: 0.5rem;
113-
}
114102

115103
/* Upload Section Layout */
116104
.upload-section {
@@ -125,16 +113,31 @@ body.main-page main {
125113
margin-bottom: 0.5rem;
126114
}
127115

116+
/* Collapse truly empty direct children to prevent stray blank rows */
117+
.upload-section>*:empty {
118+
display: none;
119+
margin: 0;
120+
padding: 0;
121+
}
122+
123+
/* API key row styling - spans full width in grid layout */
124+
.upload-section .api-key-row {
125+
margin-bottom: 0.5rem;
126+
}
127+
128128
/* Form Elements */
129129
.form-group {
130+
130131
margin-bottom: 1rem;
131132
display: flex;
132133
flex-direction: column;
133-
height: 100%;
134+
/* do not stretch to 100% of the grid row: let content size naturally */
135+
height: auto;
134136
}
135137

136138
.form-group .form-controls {
137139
margin-top: auto;
140+
padding: 0.5rem;
138141
}
139142

140143
.form-group label {
@@ -362,20 +365,14 @@ input[type="file"]:hover {
362365
.example code {
363366
display: block;
364367
background: var(--background);
365-
padding: 0.5rem;
366-
border-radius: 4px;
367-
font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Monaco, monospace;
368-
font-size: 0.75rem;
369-
color: var(--text-primary);
370-
border: 1px solid var(--border);
371-
word-break: break-all;
372368
padding: 0.75rem;
373369
border-radius: calc(var(--radius) - 2px);
374370
font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Monaco, monospace;
375371
font-size: 0.8rem;
376372
color: var(--text-primary);
377-
overflow-x: auto;
378373
border: 1px solid var(--border);
374+
word-break: break-all;
375+
overflow-x: auto;
379376
}
380377

381378
/* Responsive Design */
@@ -412,8 +409,6 @@ input[type="file"]:hover {
412409

413410
.card {
414411
padding: 1rem;
415-
margin-bottom: 1rem;
416-
/* Space between cards on mobile */
417412
}
418413

419414
.form-controls {
@@ -686,4 +681,37 @@ footer {
686681

687682
.result-section {
688683
animation: slideIn 0.3s ease-out;
684+
}
685+
686+
/* Responsive Design - Desktop (min-width: 768px) */
687+
@media (min-width: 768px) {
688+
.upload-section {
689+
gap: 0 1rem;
690+
/* row-gap column-gap */
691+
}
692+
693+
.upload-section .api-key-row {
694+
grid-column: 1 / -1;
695+
padding: 0;
696+
margin: 0;
697+
}
698+
699+
.card.upload-section .api-key-row label {
700+
display: block;
701+
margin: 0 0 0.5rem 0;
702+
padding: 0;
703+
box-sizing: border-box;
704+
}
705+
706+
.card.upload-section .api-key-row input[type="text"] {
707+
display: block;
708+
margin: 0;
709+
width: 100%;
710+
box-sizing: border-box;
711+
padding: 0.5rem;
712+
}
713+
714+
.upload-section .form-group {
715+
margin-bottom: 0.25rem;
716+
}
689717
}

static/view.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta charset="UTF-8">
66
<meta name="viewport" content="width=device-width, initial-scale=1.0">
77
<title>{{.Title}}</title>
8-
<link rel="stylesheet" href="/static/style.css">
8+
<link rel="stylesheet" href="/static/style.css?v={{.Version}}">
99
</head>
1010

1111
<body>

0 commit comments

Comments
 (0)