Compare commits

...

42 Commits

Author SHA1 Message Date
2006be0434 Merge pull request #145 from cupcakearmy/better-programmatic-access
better programmatic access
2024-08-23 11:19:57 +02:00
ca72e94e3c update node and playwright 2024-08-23 11:02:30 +02:00
dbcb3870aa fix tests 2024-08-23 11:01:57 +02:00
3ea176cc1f add build cli 2024-08-23 09:59:06 +02:00
145f9ef18f right package stuff 2024-08-22 20:21:05 +02:00
784c54236b better programmatic access 2024-08-22 20:01:14 +02:00
5648c76f78 Merge pull request #144 from cupcakearmy/update-rust
update rust
2024-08-22 19:57:39 +02:00
7761c795df update rust 2024-08-22 18:42:44 +02:00
4aadeb492a maintenance 2024-08-22 18:40:56 +02:00
0d9f3fe9c7 Merge pull request #130 from DDd-Devops/add-redis-tls-feature
Add redis tls feature 'rediss://'
2024-08-22 18:30:58 +02:00
f790438104 add french blog post 2024-06-16 22:34:44 +02:00
5936f4588c Merge pull request #133 from DDd-Devops/improve-french-translations
improve french translations wording
2024-06-16 22:31:34 +02:00
Thomas Chrétien
d3c04f8fda improve french translations wording 2024-06-14 14:26:02 +00:00
Matthieu Guegan
f8c17487bd Support dynamically-linked and/or native musl targets
See https://github.com/rust-lang/rust/pull/40113#issuecomment-323193341
2024-05-16 09:55:04 +02:00
Matthieu Guegan
ed3e5f48a0 Fix wrong type due to updated lib
See https://github.com/redis-rs/redis-rs/pull/589
2024-05-16 09:47:01 +02:00
Matthieu Guegan
e08c9d1871 Bump redis crate to 0.25.2
This will enable TLS feature
2024-05-16 09:45:26 +02:00
6d2150b0b6 version bump 2024-05-04 16:06:40 +02:00
3a68693be1 Merge pull request #128 from cbin/main
change locales
2024-04-26 13:45:17 +02:00
Oleg Salnikov
a612eec220 Add files via upload 2024-04-24 18:31:01 +03:00
98d3b0d394 Merge pull request #127 from zocimek/main
fix key for home.new_note_notice
2024-04-22 09:38:49 +02:00
Łukasz Pospiech
6aed2e2756 fix key for home.new_note_notice 2024-04-18 12:39:00 +02:00
6bb527198a Merge pull request #123 from zocimek/patch-1
Create polish translation
2024-04-10 16:57:18 +02:00
Łukasz Pospiech
7050389316 Create polish translation 2024-04-05 14:00:58 +02:00
0725a0c6f7 Merge pull request #122 from cupcakearmy/dependabot/npm_and_yarn/npm_and_yarn-security-group-e93d6eacd9
Bump the npm_and_yarn group across 1 directory with 1 update
2024-04-05 09:37:44 +02:00
dependabot[bot]
c8efcc04fc Bump the npm_and_yarn group across 1 directory with 1 update
Bumps the npm_and_yarn group with 1 update in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `vite` from 5.1.4 to 5.1.7
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.1.7/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.1.7/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
  dependency-group: npm_and_yarn-security-group
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-03 17:38:45 +00:00
19bf155653 Merge pull request #120 from MairusuPawa/patch-1
Update fr.json
2024-03-24 20:57:00 +01:00
MairusuPawa
9a4e84db62 Update fr.json
Original translation confuses "crypter" and "chiffrer".
2024-03-23 16:19:29 +01:00
32cd3843a7 version bump 2024-03-21 15:55:21 +01:00
9b48d39c82 Merge pull request #119 from compasspathways/main
Misc improvements including `ALLOW_FILES` and `NEW_NOTE_NOTICE`
2024-03-19 16:04:04 +01:00
Tamás Kádár
239e950f8e NEW_NOTE_NOTICE -> THEME_NEW_NOTE_NOTICE 2024-03-19 12:06:46 +00:00
Tamás Kádár
b00846ce9d clicking the logo after the creation of the note should do a reset 2024-03-15 14:30:55 +00:00
Tamás Kádár
e70f06f99f NEW_NOTE_NOTICE feature flag 2024-03-15 14:26:33 +00:00
Tamás Kádár
4781882c72 fix extra space in /about 2024-03-15 14:19:30 +00:00
Tamás Kádár
549dfb55db update @types/node 2024-03-15 14:16:02 +00:00
Tamás Kádár
2653a4bccf ALLOW_FILES flag 2024-03-15 14:14:17 +00:00
Tamás Kádár
7213e6c690 Minor Dockerfile fixes 2024-03-15 14:11:34 +00:00
Tamás Kádár
8a5f667ff6 Fix file-saver CommonJS error 2024-03-15 13:53:12 +00:00
fc3938701e add reset translation 2024-03-04 18:46:00 +01:00
23b4f81dac Merge pull request #118 from codiflow/codiflow-patch-1
Update de.json
2024-03-04 18:42:39 +01:00
7c68620d8b Merge branch 'main' into codiflow-patch-1 2024-03-04 18:32:46 +01:00
eb76fe085a rm pkg and update node version 2024-03-04 18:32:03 +01:00
Christian
38540b33f2 Update de.json
Fixed some translations in the German language strings
2024-03-04 17:39:35 +01:00
39 changed files with 3633 additions and 2815 deletions

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ target
# Testing
test-results
tmp

2
.nvmrc
View File

@@ -1 +1 @@
v18.19.1
v22.7.0

View File

@@ -1,5 +1,5 @@
# FRONTEND
FROM node:18-alpine as client
FROM node:22-alpine as client
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
@@ -11,17 +11,17 @@ RUN pnpm run build
# BACKEND
FROM rust:1.76-alpine as backend
FROM rust:1.80-alpine as backend
WORKDIR /tmp
RUN apk add libc-dev openssl-dev alpine-sdk
RUN apk add --no-cache libc-dev openssl-dev alpine-sdk
COPY ./packages/backend ./
RUN cargo build --release
RUN RUSTFLAGS="-Ctarget-feature=-crt-static" cargo build --release
# RUNNER
FROM alpine
FROM alpine:3.19
WORKDIR /app
RUN apk add --no-cache curl
RUN apk add --no-cache curl libgcc
COPY --from=backend /tmp/target/release/cryptgeon .
COPY --from=client /tmp/packages/frontend/build ./frontend
ENV FRONTEND_PATH="./frontend"

View File

@@ -14,7 +14,7 @@
<a href=""><img src="./.github/lokalise.png" height="50">
<br/><br/>
EN | [简体中文](README_zh-CN.md) | [ES](README_ES.md)
EN | [简体中文](README_zh-CN.md) | [ES](README_ES.md)
## About?
@@ -62,19 +62,21 @@ of the notes even if it tried to.
## Environment Variables
| Variable | Default | Description |
| ------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `REDIS` | `redis://redis/` | Redis URL to connect to. [According to format](https://docs.rs/redis/latest/redis/#connection-parameters) |
| `SIZE_LIMIT` | `1 KiB` | Max size for body. Accepted values according to [byte-unit](https://docs.rs/byte-unit/). <br> `512 MiB` is the maximum allowed. <br> The frontend will show that number including the ~35% encoding overhead. |
| `MAX_VIEWS` | `100` | Maximal number of views. |
| `MAX_EXPIRATION` | `360` | Maximal expiration in minutes. |
| `ALLOW_ADVANCED` | `true` | Allow custom configuration. If set to `false` all notes will be one view only. |
| `ID_LENGTH` | `32` | Set the size of the note `id` in bytes. By default this is `32` bytes. This is useful for reducing link size. _This setting does not affect encryption strength_. |
| `VERBOSITY` | `warn` | Verbosity level for the backend. [Possible values](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) are: `error`, `warn`, `info`, `debug`, `trace` |
| `THEME_IMAGE` | `""` | Custom image for replacing the logo. Must be publicly reachable |
| `THEME_TEXT` | `""` | Custom text for replacing the description below the logo |
| `THEME_PAGE_TITLE` | `""` | Custom text the page title |
| `THEME_FAVICON` | `""` | Custom url for the favicon. Must be publicly reachable |
| Variable | Default | Description |
| ----------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `REDIS` | `redis://redis/` | Redis URL to connect to. [According to format](https://docs.rs/redis/latest/redis/#connection-parameters) |
| `SIZE_LIMIT` | `1 KiB` | Max size for body. Accepted values according to [byte-unit](https://docs.rs/byte-unit/). <br> `512 MiB` is the maximum allowed. <br> The frontend will show that number including the ~35% encoding overhead. |
| `MAX_VIEWS` | `100` | Maximal number of views. |
| `MAX_EXPIRATION` | `360` | Maximal expiration in minutes. |
| `ALLOW_ADVANCED` | `true` | Allow custom configuration. If set to `false` all notes will be one view only. |
| `ALLOW_FILES` | `true` | Allow uploading files. If set to `false`, users will only be allowed to create text notes. |
| `THEME_NEW_NOTE_NOTICE` | `true` | Show the message about how notes are stored in the memory and may be evicted after creating a new note. Defaults to `true`. |
| `ID_LENGTH` | `32` | Set the size of the note `id` in bytes. By default this is `32` bytes. This is useful for reducing link size. _This setting does not affect encryption strength_. |
| `VERBOSITY` | `warn` | Verbosity level for the backend. [Possible values](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) are: `error`, `warn`, `info`, `debug`, `trace` |
| `THEME_IMAGE` | `""` | Custom image for replacing the logo. Must be publicly reachable |
| `THEME_TEXT` | `""` | Custom text for replacing the description below the logo |
| `THEME_PAGE_TITLE` | `""` | Custom text the page title |
| `THEME_FAVICON` | `""` | Custom url for the favicon. Must be publicly reachable |
## Deployment
@@ -139,12 +141,16 @@ There is a [guide](https://mariushosting.com/how-to-install-cryptgeon-on-your-sy
- English by [DB Tech](https://www.youtube.com/watch?v=S0jx7wpOfNM) [Previous Video](https://www.youtube.com/watch?v=JhpIatD06vE)
- German by [ApfelCast](https://www.youtube.com/watch?v=84ZMbE9AkHg)
### Written Guides
- French by [zarevskaya](https://belginux.com/installer-cryptgeon-avec-docker/)
## Development
**Requirements**
- `pnpm`: `>=6`
- `node`: `>=18`
- `pnpm`: `>=9`
- `node`: `>=22`
- `rust`: edition `2021`
**Install**

View File

@@ -1,8 +1,6 @@
# DEV Compose file.
# For a production file see: README.md
version: '3.8'
services:
redis:
image: redis:7-alpine

View File

@@ -1,5 +1,3 @@
version: '3.8'
services:
redis:
image: redis:7-alpine
@@ -16,7 +14,7 @@ services:
SIZE_LIMIT: 4 MiB
ports:
- 80:8000
# Optional health checks
# healthcheck:
# test: ["CMD", "curl", "--fail", "http://127.0.0.1:8000/api/live/"]

View File

@@ -1,6 +1,6 @@
{
"scripts": {
"dev:docker": "docker-compose -f docker-compose.dev.yaml up redis",
"dev:docker": "docker compose -f docker-compose.dev.yaml up redis",
"dev:packages": "pnpm --parallel run dev",
"dev": "run-p dev:*",
"docker:up": "docker compose -f docker-compose.dev.yaml up",
@@ -12,10 +12,10 @@
"build": "pnpm run --recursive --filter=!@cryptgeon/backend build"
},
"devDependencies": {
"@playwright/test": "^1.42.1",
"@types/node": "^20.11.24",
"@playwright/test": "^1.46.1",
"@types/node": "^22.5.0",
"npm-run-all": "^4.1.5",
"shelljs": "^0.8.5"
},
"packageManager": "pnpm@8.15.4"
"packageManager": "pnpm@9.8.0"
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
[package]
name = "cryptgeon"
version = "2.5.0"
version = "2.7.0"
authors = ["cupcakearmy <hi@nicco.io>"]
edition = "2021"
rust-version = "1.76"
rust-version = "1.80"
[[bin]]
name = "cryptgeon"
@@ -22,4 +22,4 @@ dotenv = "0.15"
mime = "0.3"
env_logger = "0.9"
log = "0.4"
redis = "0.23"
redis = { version = "0.25.2", features = ["tls-native-tls"] }

View File

@@ -14,26 +14,34 @@ lazy_static! {
// CONFIG
lazy_static! {
pub static ref LIMIT: usize =
Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string()))
.unwrap()
.get_bytes() as usize;
pub static ref MAX_VIEWS: u32 = std::env::var("MAX_VIEWS")
.unwrap_or("100".to_string())
.parse()
.unwrap();
pub static ref MAX_EXPIRATION: u32 = std::env::var("MAX_EXPIRATION")
.unwrap_or("360".to_string()) // 6 hours in minutes
.parse()
.unwrap();
pub static ref ALLOW_ADVANCED: bool = std::env::var("ALLOW_ADVANCED")
.unwrap_or("true".to_string())
.parse()
.unwrap();
pub static ref ID_LENGTH: u32 = std::env::var("ID_LENGTH")
.unwrap_or("32".to_string())
.parse()
.unwrap();
pub static ref LIMIT: usize =
Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string()))
.unwrap()
.get_bytes() as usize;
pub static ref MAX_VIEWS: u32 = std::env::var("MAX_VIEWS")
.unwrap_or("100".to_string())
.parse()
.unwrap();
pub static ref MAX_EXPIRATION: u32 = std::env::var("MAX_EXPIRATION")
.unwrap_or("360".to_string()) // 6 hours in minutes
.parse()
.unwrap();
pub static ref ALLOW_ADVANCED: bool = std::env::var("ALLOW_ADVANCED")
.unwrap_or("true".to_string())
.parse()
.unwrap();
pub static ref ID_LENGTH: u32 = std::env::var("ID_LENGTH")
.unwrap_or("32".to_string())
.parse()
.unwrap();
pub static ref ALLOW_FILES: bool = std::env::var("ALLOW_FILES")
.unwrap_or("true".to_string())
.parse()
.unwrap();
pub static ref THEME_NEW_NOTE_NOTICE: bool = std::env::var("THEME_NEW_NOTE_NOTICE")
.unwrap_or("true".to_string())
.parse()
.unwrap();
}
// THEME

View File

@@ -9,6 +9,8 @@ pub struct Status {
pub max_views: u32,
pub max_expiration: u32,
pub allow_advanced: bool,
pub allow_files: bool,
pub theme_new_note_notice: bool,
// Theme
pub theme_image: String,
pub theme_text: String,

View File

@@ -11,10 +11,12 @@ async fn get_status() -> impl Responder {
max_views: *config::MAX_VIEWS,
max_expiration: *config::MAX_EXPIRATION,
allow_advanced: *config::ALLOW_ADVANCED,
allow_files: *config::ALLOW_FILES,
theme_new_note_notice: *config::THEME_NEW_NOTE_NOTICE,
theme_image: config::THEME_IMAGE.to_string(),
theme_text: config::THEME_TEXT.to_string(),
theme_page_title: config::THEME_PAGE_TITLE.to_string(),
theme_favicon: config::THEME_FAVICON.to_string()
theme_favicon: config::THEME_FAVICON.to_string(),
});
}

View File

@@ -36,7 +36,7 @@ pub fn set(id: &String, note: &Note) -> Result<(), &'static str> {
match note.expiration {
Some(e) => {
let seconds = e - now();
conn.expire(id, seconds as usize)
conn.expire(id, seconds as i64)
.map_err(|_| "Unable to set expiration on notion")?
}
None => {}

14
packages/cli/build.js Normal file
View File

@@ -0,0 +1,14 @@
import pkg from './package.json' with { type: 'json' }
import { build } from 'tsup'
const watch = process.argv.slice(2)[0] === '--watch'
await build({
entry: ['src/index.ts', 'src/cli.ts'],
dts: true,
minify: true,
format: ['esm', 'cjs'],
clean: true,
define: { VERSION: `"${pkg.version}"` },
watch,
})

View File

@@ -1,6 +1,6 @@
{
"name": "cryptgeon",
"version": "2.5.0",
"version": "2.7.0",
"homepage": "https://github.com/cupcakearmy/cryptgeon",
"repository": {
"type": "git",
@@ -9,10 +9,7 @@
},
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
".": "./dist/index.js"
},
"types": "./dist/index.d.ts",
"bin": {
@@ -23,9 +20,8 @@
],
"scripts": {
"bin": "run-s build package",
"build": "tsc && ./scripts/build.js",
"dev": "./scripts/build.js --watch",
"package": "./scripts/package.js",
"build": "tsc && node build.js",
"dev": "node build.js --watch",
"prepublishOnly": "run-s build"
},
"devDependencies": {
@@ -35,12 +31,11 @@
"@types/mime": "^3.0.4",
"@types/node": "^20.11.24",
"commander": "^12.0.0",
"esbuild": "^0.20.1",
"inquirer": "^9.2.15",
"mime": "^4.0.1",
"occulto": "^2.0.3",
"pkg": "^5.8.1",
"pretty-bytes": "^6.1.1",
"tsup": "^8.2.4",
"typescript": "^5.3.3"
},
"engines": {

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env node
import { build, context } from 'esbuild'
import pkg from '../package.json' assert { type: 'json' }
const options = {
entryPoints: ['./src/cli.ts'],
bundle: true,
minify: true,
platform: 'node',
outfile: './dist/cli.cjs',
define: { VERSION: `"${pkg.version}"` },
}
const watch = process.argv.slice(2)[0] === '--watch'
if (watch) {
;(await context(options)).watch()
} else {
await build(options)
// Also build internals to expose
await build({
entryPoints: ['./src/index.ts'],
bundle: true,
minify: true,
platform: 'node',
format: 'esm',
outfile: './dist/index.js',
define: { VERSION: `"${pkg.version}"` },
})
}

View File

@@ -1,17 +0,0 @@
#!/usr/bin/env node
import { exec } from 'pkg'
const targets = [
'node18-macos-arm64',
'node18-macos-x64',
'node18-linux-arm64',
'node18-linux-x64',
'node18-win-arm64',
'node18-win-x64',
]
for (const target of targets) {
console.log(`🚀 Building ${target}`)
await exec(['./dist/cli.cjs', '--target', target, '--output', `./bin/${target.replace('node18', 'cryptgeon')}`])
}

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env node
import { Argument, Option, program } from '@commander-js/extra-typings'
import { setBase, status } from '@cryptgeon/shared'
import { setOptions, status } from '@cryptgeon/shared'
import prettyBytes from 'pretty-bytes'
import { download } from './download.js'
@@ -33,7 +33,7 @@ program
.description('show information about the server')
.addOption(server)
.action(async (options) => {
setBase(options.server)
setOptions({ server: options.server })
const response = await status()
const formatted = {
...response,
@@ -54,7 +54,7 @@ send
.addOption(minutes)
.addOption(password)
.action(async (files, options) => {
setBase(options.server!)
setOptions({ server: options.server })
await checkConstrains(options)
options.password ||= await getStdin()
try {
@@ -72,7 +72,7 @@ send
.addOption(minutes)
.addOption(password)
.action(async (text, options) => {
setBase(options.server!)
setOptions({ server: options.server })
await checkConstrains(options)
options.password ||= await getStdin()
try {

View File

@@ -1,4 +1,4 @@
import { Adapters, get, info, setBase } from '@cryptgeon/shared'
import { Adapters, get, info, setOptions } from '@cryptgeon/shared'
import inquirer from 'inquirer'
import { access, constants, writeFile } from 'node:fs/promises'
import { basename, resolve } from 'node:path'
@@ -6,7 +6,7 @@ import { AES, Hex } from 'occulto'
import pretty from 'pretty-bytes'
export async function download(url: URL, all: boolean, suggestedPassword?: string) {
setBase(url.origin)
setOptions({ server: url.origin })
const id = url.pathname.split('/')[2]
const preview = await info(id).catch(() => {
throw new Error('Note does not exist or is expired')

View File

@@ -1,7 +1,7 @@
import { readFile, stat } from 'node:fs/promises'
import { basename } from 'node:path'
import { Adapters, BASE, create, FileDTO, Note, NoteMeta } from '@cryptgeon/shared'
import { Adapters, create, getOptions, FileDTO, Note, NoteMeta } from '@cryptgeon/shared'
import mime from 'mime'
import { AES, Hex } from 'occulto'
@@ -39,7 +39,7 @@ export async function upload(input: string | string[], options: UploadOptions):
// Create the actual note and upload it.
const note: Note = { ...noteOptions, contents, meta: { type, derivation: derived?.[1] } }
const result = await create(note)
let url = `${BASE}/note/${result.id}`
let url = `${getOptions().server}/note/${result.id}`
if (!derived) url += `#${Hex.encode(key)}`
return url
}

View File

@@ -3,24 +3,24 @@
"note": "Notiz",
"file": "Datei",
"advanced": "Erweiterte Optionen",
"create": "Notiz erstellen",
"create": "Erstellen",
"loading": "Lädt...",
"mode": "Modus",
"views": "{n, plural, =0 {Ansichten} =1 {1 Ansicht} other {# Ansichten}}",
"minutes": "{n, plural, =0 {Minuten} =1 {1 Minute} other {# Minuten}}",
"max": "max",
"share_link": "Link teilen",
"copy_clipboard": "Link in die Zwischenablage kopieren",
"copied_to_clipboard": "Link in die Zwischenablage kopiert.",
"encrypting": "Notiz wird verschlüsselt...",
"decrypting": "Notiz wird entschlüsselt...",
"copy_clipboard": "In die Zwischenablage kopieren",
"copied_to_clipboard": "In die Zwischenablage kopiert.",
"encrypting": "Wird verschlüsselt...",
"decrypting": "Wird entschlüsselt...",
"uploading": "Hochladen",
"downloading": "Wird heruntergeladen",
"qr_code": "QR-Code",
"password": "Passwort"
},
"home": {
"intro": "Senden Sie mit ganz einfach einem Klick <i>vollständig verschlüsselte</i>, sichere Notizen oder Dateien. Erstellen Sie einfach eine Notiz und teilen Sie den Link.",
"intro": "Erstellen Sie mit einem Klick <i>vollständig verschlüsselte</i>, sichere Notizen oder Dateien und teilen Sie diese über einen Link.",
"explanation": "Die Notiz verfällt nach {type}.",
"new_note": "Neue Notiz",
"new_note_notice": "<b>Wichtiger Hinweis zur Verfügbarkeit:</b><br />Es kann nicht garantiert werden, dass diese Notiz gespeichert wird, da diese <b>ausschließlich im Speicher</b> gehalten werden. Ist dieser voll, werden die ältesten Notizen entfernt.<br />(Wahrscheinlich gibt es keine derartigen Probleme, seien Sie nur vorgewarnt).",
@@ -34,24 +34,25 @@
"note_created": "Notiz wurde erstellt."
},
"advanced": {
"explanation": "Standardmäßig wird für jede Notiz ein sicher generiertes Passwort verwendet. Sie können alternativ ein eigenes Kennwort wählen, das nicht im Link enthalten ist.",
"explanation": "Standardmäßig wird für jede Notiz ein generiertes, sicheres Passwort verwendet. Alternativ können Sie ein eigenes Kennwort festlegen, welches nicht im Link enthalten ist.",
"custom_password": "Benutzerdefiniertes Passwort"
}
},
"show": {
"errors": {
"not_found": "Notiz konnte nicht gefunden werden oder wurde bereits gelöscht.",
"decryption_failed": "Notiz konnte nicht entschlüsselt werden. Vermutlich ist das Passwort falsch oder der Link defekt. Notiz wurde zerstört.",
"decryption_failed": "Notiz konnte nicht entschlüsselt werden. Vermutlich ist das Passwort falsch oder der Link defekt. Die Notiz wurde daher gelöscht.",
"unsupported_type": "Nicht unterstützter Notiztyp."
},
"explanation": "Klicken Sie unten, um die Notiz anzuzeigen und anschließend zu löschen, falls das Limit erreicht ist.",
"explanation": "Klicken Sie auf den Button, um die Notiz anzuzeigen und anschließend zu löschen, falls ein festgelegtes Limit erreicht wurde.",
"show_note": "Notiz anzeigen",
"warning_will_not_see_again": "Achtung! Sie haben anschließend <b>keine</b> Gelegenheit, die Notiz noch einmal anzusehen.",
"warning_will_not_see_again": "ACHTUNG! Sie werden anschließend <b>keine</b> Gelegenheit mehr haben, die Notiz erneut anzusehen.",
"download_all": "Alle Dateien herunterladen",
"links_found": "Links in der Notiz:"
"links_found": "Gefundene Links in der Notiz:"
},
"file_upload": {
"selected_files": "Ausgewählte Dateien",
"no_files_selected": "Keine Dateien ausgewählt"
"no_files_selected": "Keine Dateien ausgewählt",
"clear": "Zurücksetzen"
}
}

View File

@@ -52,6 +52,7 @@
},
"file_upload": {
"selected_files": "Selected Files",
"no_files_selected": "No Files Selected"
"no_files_selected": "No Files Selected",
"clear": "Reset"
}
}

View File

@@ -52,6 +52,7 @@
},
"file_upload": {
"selected_files": "Archivos seleccionados",
"no_files_selected": "No hay archivos seleccionados"
"no_files_selected": "No hay archivos seleccionados",
"clear": "Restablecer"
}
}

View File

@@ -12,20 +12,20 @@
"share_link": "partager le lien",
"copy_clipboard": "copier dans le presse-papiers",
"copied_to_clipboard": "copié dans le presse-papiers",
"encrypting": "cryptage",
"encrypting": "chiffrer",
"decrypting": "déchiffrer",
"uploading": "téléchargement",
"uploading": "téléversement",
"downloading": "téléchargement",
"qr_code": "code qr",
"password": "mot de passe"
},
"home": {
"intro": "Envoyez facilement des notes ou des fichiers <i>entièrement cryptés</i> et sécurisés en un seul clic. Il suffit de créer une note et de partager le lien.",
"intro": "Envoyez facilement des notes ou des fichiers <i>entièrement chiffrés</i> et sécurisés en un seul clic. Il suffit de créer une note et de partager le lien.",
"explanation": "la note expirera et sera détruite après {type}.",
"new_note": "nouvelle note",
"new_note_notice": "<b>disponibilité :</b><br />la note n'est pas garantie d'être stockée car tout est conservé dans la mémoire vive, si elle se remplit les notes les plus anciennes seront supprimées.<br />(vous serez probablement bien, soyez juste averti.)",
"new_note_notice": "<b>disponibilité :</b><br />il n'est pas garanti que la note reste stockée car tout est conservé dans la mémoire vive; si elle se remplit, les notes les plus anciennes seront supprimées.<br />(tout ira probablement bien, soyez juste averti.)",
"errors": {
"note_to_big": "Impossible de créer une note. La note est trop grande",
"note_to_big": "Impossible de créer une note. La note est trop grande.",
"note_error": "n'a pas pu créer de note. Veuillez réessayer.",
"max": "max: {n}",
"empty_content": "La note est vide."
@@ -45,13 +45,14 @@
"unsupported_type": "type de note non supporté."
},
"explanation": "Cliquez ci-dessous pour afficher et supprimer la note si le compteur a atteint sa limite.",
"show_note": "note de présentation",
"show_note": "afficher la note",
"warning_will_not_see_again": "vous <b>n'aurez pas</b> la chance de revoir la note.",
"download_all": "télécharger tout",
"download_all": "tout télécharger",
"links_found": "liens trouvés à lintérieur de la note :"
},
"file_upload": {
"selected_files": "Fichiers sélectionnés",
"no_files_selected": "Aucun fichier sélectionné"
"no_files_selected": "Aucun fichier sélectionné",
"clear": "Réinitialiser"
}
}

View File

@@ -52,6 +52,7 @@
},
"file_upload": {
"selected_files": "File selezionati",
"no_files_selected": "Nessun file selezionato"
"no_files_selected": "Nessun file selezionato",
"clear": "Reset"
}
}

View File

@@ -52,6 +52,7 @@
},
"file_upload": {
"selected_files": "選択したファイル",
"no_files_selected": "ファイルが選択されていません"
"no_files_selected": "ファイルが選択されていません",
"clear": "リセット"
}
}

View File

@@ -0,0 +1,58 @@
{
"common": {
"note": "notatka",
"file": "plik",
"advanced": "zaawansowane",
"create": "utwórz",
"loading": "ładowanie",
"mode": "tryb",
"views": "{n, plural, =0 {wyświetleń} =1 {1 wyświetlenie} other {# wyświetleń}}",
"minutes": "{n, plural, =0 {minut} =1 {1 minuta} other {# minuty}}",
"max": "maks.",
"share_link": "link udostępniania",
"copy_clipboard": "kopiuj do schowka",
"copied_to_clipboard": "skopiowano do schowka",
"encrypting": "szyfrowanie",
"decrypting": "odszyfrowywanie",
"uploading": "wysyłanie",
"downloading": "pobieranie",
"qr_code": "kod QR",
"password": "hasło"
},
"home": {
"intro": "Łatwo wysyłaj <i>w pełni zaszyfrowane</i>, bezpieczne notatki lub pliki jednym kliknięciem. Po prostu utwórz notatkę i udostępnij link.",
"explanation": "notatka wygaśnie i zostanie zniszczona po {type}.",
"new_note": "nowa notatka",
"new_note_notice": "<b>dostępność:</b><br />nie ma gwarancji, że notatka będzie przechowywana, ponieważ wszystko jest przechowywane w pamięci RAM, jeśli się zapełni, najstarsze notatki zostaną usunięte.<br />(prawdopodobnie nic się nie stanie, ale warto ostrzec.)",
"errors": {
"note_to_big": "nie można utworzyć notatki. notatka jest za duża",
"note_error": "nie można utworzyć notatki. spróbuj ponownie.",
"max": "maks .: {n}",
"empty_content": "notatka jest pusta."
},
"messages": {
"note_created": "notatka utworzona."
},
"advanced": {
"explanation": "Domyślnie dla każdej notatki używane jest bezpiecznie wygenerowane hasło. Możesz jednak wybrać własne hasło, które nie jest uwzględnione w linku.",
"custom_password": "własne hasło"
}
},
"show": {
"errors": {
"not_found": "notatka nie została znaleziona lub została już usunięta.",
"decryption_failed": "błędne hasło. nie można odszyfrować. prawdopodobnie uszkodzony link. notatka została zniszczona.",
"unsupported_type": "nieobsługiwany typ notatki."
},
"explanation": "kliknij poniżej, aby wyświetlić i usunąć notatkę, jeśli licznik osiągnie swój limit",
"show_note": "pokaż notatkę",
"warning_will_not_see_again": "<b>nie będziesz mieć</b> możliwości ponownego zobaczenia notatki.",
"download_all": "pobierz wszystko",
"links_found": "linki znalezione w notatce:"
},
"file_upload": {
"selected_files": "Wybrane pliki",
"no_files_selected": "Nie wybrano plików",
"clear": "Wyczyść"
}
}

View File

@@ -1,57 +1,58 @@
{
"common": {
"note": "заметка",
"file": "файл",
"advanced": "расширенные",
"create": "создать",
"loading": "загрузка",
"mode": "режим",
"views": "{n, plural, =0 {просмотры} =1 {1 просмотр} other {# просмотры}}",
"minutes": "{n, plural, =0 {минут} =1 {1 минута} other {# минуты}}",
"max": "макс",
"share_link": "поделиться ссылкой",
"copy_clipboard": "скопировать в буфер обмена",
"copied_to_clipboard": "скопировано в буфер обмена",
"encrypting": "шифрование",
"decrypting": "расшифровка",
"uploading": "загрузка",
"downloading": "скачивание",
"qr_code": "qr код",
"password": "пароль"
},
"home": {
"intro": "Легко отправляйте <i>полностью зашифрованные</i> защищенные заметки или файлы одним щелчком мыши. Просто создайте заметку и поделитесь ссылкой.",
"explanation": "заметка истечет и будет уничтожена после {type}.",
"new_note": "новая заметка",
"new_note_notice": "<b>availability:</b><br />the note is not guaranteed to be stored as everything is kept in ram, if it fills up the oldest notes will be removed.<br />(you probably will be fine, just be warned.)",
"errors": {
"note_to_big": "нельзя создать новую заметку. заметка слишком большая",
"note_error": "нельзя создать новую заметку. пожалйста попробуйте позднее.",
"max": "макс: {n}",
"empty_content": "пустая заметка."
},
"messages": {
"note_created": "заметка создана."
},
"advanced": {
"explanation": "По умолчанию для каждой заметки используется безопасно сгенерированный пароль. Однако вы также можете выбрать свой собственный пароль, который не включен в ссылку.",
"custom_password": "пользовательский пароль"
}
},
"show": {
"errors": {
"not_found": "заметка не найдена или была удалена.",
"decryption_failed": "неправильный пароль. не смог расшифровать. возможно ссылка битая. записка уничтожена.",
"unsupported_type": "неподдерживаемый тип заметки."
},
"explanation": "щелкните ниже, чтобы показать и удалить примечание, если счетчик достиг предела",
"show_note": "показать заметку",
"warning_will_not_see_again": "вы <b>не сможете</b> больше просмотреть заметку.",
"download_all": "скачать всё",
"links_found": "ссылки внутри заметки:"
},
"file_upload": {
"selected_files": "Выбранные файлы",
"no_files_selected": "Файлы не выбраны"
}
}
{
"common": {
"note": "заметка",
"file": "файл",
"advanced": "расширенные",
"create": "создать",
"loading": "загрузка",
"mode": "режим",
"views": "{n, plural, =0 {просмотры} =1 {1 просмотр} other {# просмотры}}",
"minutes": "{n, plural, =0 {минут} =1 {1 минута} other {# минуты}}",
"max": "макс",
"share_link": "поделиться ссылкой",
"copy_clipboard": "скопировать в буфер обмена",
"copied_to_clipboard": "скопировано в буфер обмена",
"encrypting": "шифрование",
"decrypting": "расшифровка",
"uploading": "загрузка",
"downloading": "скачивание",
"qr_code": "qr код",
"password": "пароль"
},
"home": {
"intro": "Легко отправляйте <i>полностью зашифрованные</i> защищенные заметки или файлы одним щелчком мыши. Просто создайте заметку и поделитесь ссылкой.",
"explanation": "заметка истечет и будет уничтожена после {type}.",
"new_note": "новая заметка",
"new_note_notice": "<b>доступность:</b><br />сохранение заметки не гарантируется, поскольку все хранится в оперативной памяти; если она заполнится, самые старые заметки будут удалены.<br />( вероятно, все будет в порядке, просто будьте осторожны.)",
"errors": {
"note_to_big": "нельзя создать новую заметку. заметка слишком большая",
"note_error": "нельзя создать новую заметку. пожалйста попробуйте позднее.",
"max": "макс: {n}",
"empty_content": "пустая заметка."
},
"messages": {
"note_created": "заметка создана."
},
"advanced": {
"explanation": "По умолчанию для каждой заметки используется безопасно сгенерированный пароль. Однако вы также можете выбрать свой собственный пароль, который не включен в ссылку.",
"custom_password": "пользовательский пароль"
}
},
"show": {
"errors": {
"not_found": "заметка не найдена или была удалена.",
"decryption_failed": "неправильный пароль. не смог расшифровать. возможно ссылка битая. записка уничтожена.",
"unsupported_type": "неподдерживаемый тип заметки."
},
"explanation": "щелкните ниже, чтобы показать и удалить примечание, если счетчик достиг предела",
"show_note": "показать заметку",
"warning_will_not_see_again": "вы <b>не сможете</b> больше просмотреть заметку.",
"download_all": "скачать всё",
"links_found": "ссылки внутри заметки:"
},
"file_upload": {
"selected_files": "Выбранные файлы",
"no_files_selected": "Файлы не выбраны",
"clear": "Сброс"
}
}

View File

@@ -52,6 +52,7 @@
},
"file_upload": {
"selected_files": "已选中的文件",
"no_files_selected": "没有文件被选中"
"no_files_selected": "没有文件被选中",
"clear": "重置"
}
}

View File

@@ -26,7 +26,7 @@
"svelte-intl-precompile": "^0.12.3",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"vite": "^5.1.4"
"vite": "^5.1.7"
},
"dependencies": {
"@cryptgeon/shared": "workspace:*",

View File

@@ -46,7 +46,7 @@
</div>
{/each}
<div class="spacer" />
<Button on:click={clear}>Clear</Button>
<Button on:click={clear}>{$t('file_upload.clear')}</Button>
</div>
{:else}
<div>

View File

@@ -7,7 +7,7 @@
<script lang="ts">
import { t } from 'svelte-intl-precompile'
import { status } from '$lib/stores/status'
import Button from '$lib/ui/Button.svelte'
import TextInput from '$lib/ui/TextInput.svelte'
import Canvas from './Canvas.svelte'
@@ -35,9 +35,11 @@
<Canvas value={url} />
</div>
<p>
{@html $t('home.new_note_notice')}
</p>
{#if $status?.theme_new_note_notice}
<p>
{@html $t('home.new_note_notice')}
</p>
{/if}
<br />
<Button on:click={reset}>{$t('home.new_note')}</Button>

View File

@@ -3,7 +3,8 @@
</script>
<script lang="ts">
import { saveAs } from 'file-saver'
import pkg from 'file-saver'
const { saveAs } = pkg
import prettyBytes from 'pretty-bytes'
import { t } from 'svelte-intl-precompile'

View File

@@ -118,12 +118,14 @@
{/if}
<div class="bottom">
<Switch
data-testid="switch-file"
class="file"
label={$t('common.file')}
bind:value={isFile}
/>
{#if $status?.allow_files}
<Switch
data-testid="switch-file"
class="file"
label={$t('common.file')}
bind:value={isFile}
/>
{/if}
{#if $status?.allow_advanced}
<Switch
data-testid="switch-advanced"

View File

@@ -1,9 +1,13 @@
<script lang="ts">
import { status } from '$lib/stores/status'
function reset() {
window.location.reload()
}
</script>
<header>
<a href="/">
<a on:click={reset} href="/">
{#if $status?.theme_image}
<img alt="logo" src={$status.theme_image} />
{:else}

View File

@@ -40,8 +40,8 @@
<br />
you are welcomed to check & audit the
<a href="https://github.com/cupcakearmy/cryptgeon" target="_blank" rel="noopener noreferrer">
source code
</a>.
source code</a
>.
</span>
</AboutParagraph>

View File

@@ -23,6 +23,10 @@ export type EncryptedFileDTO = Omit<FileDTO, 'contents'> & {
contents: string
}
type ClientOptions = {
server: string
}
type CallOptions = {
url: string
method: string
@@ -31,14 +35,21 @@ type CallOptions = {
export class PayloadToLargeError extends Error {}
export let BASE = ''
export let client: ClientOptions = {
server: '',
}
export function setBase(url: string) {
BASE = url
export function setOptions(options: Partial<ClientOptions>) {
client = { ...client, ...options }
}
export function getOptions(): ClientOptions {
return client
}
export async function call(options: CallOptions) {
const response = await fetch(BASE + '/api/' + options.url, {
const url = client.server + '/api/' + options.url
const response = await fetch(url, {
method: options.method,
body: options.body === undefined ? undefined : JSON.stringify(options.body),
mode: 'cors',

View File

@@ -4,12 +4,13 @@ const config: PlaywrightTestConfig = {
use: {
video: 'retain-on-failure',
baseURL: 'http://localhost:1234',
actionTimeout: 60_000,
actionTimeout: 10_000,
},
outputDir: './test-results',
testDir: './test',
timeout: 60_000,
timeout: 10_000,
fullyParallel: true,
webServer: {
command: 'docker compose -f docker-compose.dev.yaml up',

5126
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff