mirror of
https://github.com/cupcakearmy/cryptgeon.git
synced 2025-09-07 09:50:39 +00:00
Compare commits
25 Commits
a5da51fb2c
...
v2.8.1
Author | SHA1 | Date | |
---|---|---|---|
7c6ba654f6 | |||
cb20224317 | |||
085b1c20df | |||
4b1f939281 | |||
7b919f2a53 | |||
868b49c1c3 | |||
6b5eea34a1 | |||
d70bee14af | |||
4960260076 | |||
3247a0cfca | |||
9527a499ed | |||
b5590bb5ef | |||
7691dc80f8 | |||
f8d8fa05b0 | |||
7aae690850 | |||
e15d9eb537 | |||
7fe70a6f3b | |||
d86ef79abd | |||
23446a4c74 | |||
ee92928d1f | |||
b60a079bbe | |||
ba474dff31 | |||
3cb002ee33 | |||
a10d8735dd | |||
|
8ccfdd6e2e |
13
.github/workflows/test.yaml
vendored
13
.github/workflows/test.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
# Node
|
# Node
|
||||||
- uses: pnpm/action-setup@v3
|
- uses: pnpm/action-setup@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -27,13 +27,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
pnpm install --frozen-lockfile
|
pnpm install
|
||||||
pnpm exec playwright install --with-deps
|
pnpm exec playwright install --with-deps
|
||||||
pnpm run test:prepare
|
pnpm run test:prepare
|
||||||
|
|
||||||
- name: Run your tests
|
- name: Run your tests
|
||||||
run: pnpm test
|
run: pnpm test
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
with:
|
with:
|
||||||
name: test-results
|
name: playwright-report
|
||||||
path: test-results
|
path: test-results/
|
||||||
|
retention-days: 7
|
||||||
|
@@ -70,13 +70,13 @@ of the notes even if it tried to.
|
|||||||
| `MAX_EXPIRATION` | `360` | Maximal expiration in minutes. |
|
| `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_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. |
|
| `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_. |
|
| `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` |
|
| `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_IMAGE` | `""` | Custom image for replacing the logo. Must be publicly reachable |
|
||||||
| `THEME_TEXT` | `""` | Custom text for replacing the description below the logo |
|
| `THEME_TEXT` | `""` | Custom text for replacing the description below the logo |
|
||||||
| `THEME_PAGE_TITLE` | `""` | Custom text the page title |
|
| `THEME_PAGE_TITLE` | `""` | Custom text the page title |
|
||||||
| `THEME_FAVICON` | `""` | Custom url for the favicon. Must be publicly reachable |
|
| `THEME_FAVICON` | `""` | Custom url for the favicon. Must be publicly reachable |
|
||||||
|
| `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`. |
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
@@ -144,6 +144,8 @@ There is a [guide](https://mariushosting.com/how-to-install-cryptgeon-on-your-sy
|
|||||||
### Written Guides
|
### Written Guides
|
||||||
|
|
||||||
- French by [zarevskaya](https://belginux.com/installer-cryptgeon-avec-docker/)
|
- French by [zarevskaya](https://belginux.com/installer-cryptgeon-avec-docker/)
|
||||||
|
- Italian by [@nicfab](https://notes.nicfab.eu/it/posts/cryptgeon/)
|
||||||
|
- English by [@nicfab](https://notes.nicfab.eu/en/posts/cryptgeon/)
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
@@ -11,9 +11,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "packages/cli"
|
"path": "packages/cli"
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "packages/shared"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
|
@@ -5,8 +5,8 @@
|
|||||||
"dev": "run-p dev:*",
|
"dev": "run-p dev:*",
|
||||||
"docker:up": "docker compose -f docker-compose.dev.yaml up",
|
"docker:up": "docker compose -f docker-compose.dev.yaml up",
|
||||||
"docker:build": "docker compose -f docker-compose.dev.yaml build",
|
"docker:build": "docker compose -f docker-compose.dev.yaml build",
|
||||||
"test": "playwright test --project chrome firefox safari",
|
"test": "playwright test --project=chrome --project=firefox --project=safari",
|
||||||
"test:local": "playwright test --project chrome",
|
"test:local": "playwright test --project=chrome",
|
||||||
"test:server": "run-s docker:up",
|
"test:server": "run-s docker:up",
|
||||||
"test:prepare": "run-p build docker:build",
|
"test:prepare": "run-p build docker:build",
|
||||||
"build": "pnpm run --recursive --filter=!@cryptgeon/backend build"
|
"build": "pnpm run --recursive --filter=!@cryptgeon/backend build"
|
||||||
@@ -17,5 +17,5 @@
|
|||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"shelljs": "^0.8.5"
|
"shelljs": "^0.8.5"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.8.0"
|
"packageManager": "pnpm@9.9.0"
|
||||||
}
|
}
|
||||||
|
2
packages/backend/Cargo.lock
generated
2
packages/backend/Cargo.lock
generated
@@ -261,7 +261,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cryptgeon"
|
name = "cryptgeon"
|
||||||
version = "2.7.0"
|
version = "2.8.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"bs62",
|
"bs62",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cryptgeon"
|
name = "cryptgeon"
|
||||||
version = "2.7.0"
|
version = "2.8.1"
|
||||||
authors = ["cupcakearmy <hi@nicco.io>"]
|
authors = ["cupcakearmy <hi@nicco.io>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
use axum::extract::Path;
|
use axum::{
|
||||||
use axum::http::StatusCode;
|
extract::Path,
|
||||||
use axum::response::{IntoResponse, Response};
|
http::StatusCode,
|
||||||
use axum::Json;
|
response::{IntoResponse, Response},
|
||||||
|
Json,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
use crate::config;
|
use crate::config;
|
||||||
use axum::http::StatusCode;
|
use axum::{http::StatusCode, Json};
|
||||||
use axum::Json;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
import pkg from './package.json' with { type: 'json' }
|
|
||||||
import { build } from 'tsup'
|
import { build } from 'tsup'
|
||||||
|
import pkg from './package.json' with { type: 'json' }
|
||||||
|
|
||||||
const watch = process.argv.slice(2)[0] === '--watch'
|
const watch = process.argv.slice(2)[0] === '--watch'
|
||||||
|
|
||||||
await build({
|
await build({
|
||||||
entry: ['src/index.ts', 'src/cli.ts'],
|
entry: ['src/index.ts', 'src/cli.ts', 'src/shared/shared.ts'],
|
||||||
dts: true,
|
dts: true,
|
||||||
minify: true,
|
minify: true,
|
||||||
format: ['esm', 'cjs'],
|
format: ['esm', 'cjs'],
|
||||||
|
target: 'es2020',
|
||||||
clean: true,
|
clean: true,
|
||||||
define: { VERSION: `"${pkg.version}"` },
|
define: { VERSION: `"${pkg.version}"` },
|
||||||
watch,
|
watch,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cryptgeon",
|
"name": "cryptgeon",
|
||||||
"version": "2.7.0",
|
"version": "2.8.1",
|
||||||
"homepage": "https://github.com/cupcakearmy/cryptgeon",
|
"homepage": "https://github.com/cupcakearmy/cryptgeon",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -9,7 +9,11 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./dist/index.js"
|
".": "./dist/index.js",
|
||||||
|
"./shared": {
|
||||||
|
"import": "./dist/shared/shared.js",
|
||||||
|
"types": "./dist/shared/shared.d.ts"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -25,15 +29,14 @@
|
|||||||
"prepublishOnly": "run-s build"
|
"prepublishOnly": "run-s build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commander-js/extra-typings": "^12.0.1",
|
"@commander-js/extra-typings": "^12.1.0",
|
||||||
"@cryptgeon/shared": "workspace:*",
|
|
||||||
"@types/inquirer": "^9.0.7",
|
"@types/inquirer": "^9.0.7",
|
||||||
"@types/mime": "^3.0.4",
|
"@types/mime": "^4.0.0",
|
||||||
"@types/node": "^20.11.24",
|
"@types/node": "^20.11.24",
|
||||||
"commander": "^12.0.0",
|
"commander": "^12.1.0",
|
||||||
"inquirer": "^9.2.15",
|
"inquirer": "^9.2.15",
|
||||||
"mime": "^4.0.1",
|
"mime": "^4.0.1",
|
||||||
"occulto": "^2.0.3",
|
"occulto": "^2.0.6",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
"tsup": "^8.2.4",
|
"tsup": "^8.2.4",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
import { Adapters, get, info, setOptions } from '@cryptgeon/shared'
|
|
||||||
import inquirer from 'inquirer'
|
import inquirer from 'inquirer'
|
||||||
import { access, constants, writeFile } from 'node:fs/promises'
|
import { access, constants, writeFile } from 'node:fs/promises'
|
||||||
import { basename, resolve } from 'node:path'
|
import { basename, resolve } from 'node:path'
|
||||||
import { AES, Hex } from 'occulto'
|
import { AES, Hex } from 'occulto'
|
||||||
import pretty from 'pretty-bytes'
|
import pretty from 'pretty-bytes'
|
||||||
|
import { Adapters } from '../shared/adapters.js'
|
||||||
|
import { API } from '../shared/api.js'
|
||||||
|
|
||||||
export async function download(url: URL, all: boolean, suggestedPassword?: string) {
|
export async function download(url: URL, all: boolean, suggestedPassword?: string) {
|
||||||
setOptions({ server: url.origin })
|
API.setOptions({ server: url.origin })
|
||||||
const id = url.pathname.split('/')[2]
|
const id = url.pathname.split('/')[2]
|
||||||
const preview = await info(id).catch(() => {
|
const preview = await API.info(id).catch(() => {
|
||||||
throw new Error('Note does not exist or is expired')
|
throw new Error('Note does not exist or is expired')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ export async function download(url: URL, all: boolean, suggestedPassword?: strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
const key = derivation ? (await AES.derive(password, derivation))[0] : Hex.decode(password)
|
const key = derivation ? (await AES.derive(password, derivation))[0] : Hex.decode(password)
|
||||||
const note = await get(id)
|
const note = await API.get(id)
|
||||||
|
|
||||||
const couldNotDecrypt = new Error('Could not decrypt note. Probably an invalid password')
|
const couldNotDecrypt = new Error('Could not decrypt note. Probably an invalid password')
|
||||||
switch (note.meta.type) {
|
switch (note.meta.type) {
|
@@ -1,9 +1,10 @@
|
|||||||
import { readFile, stat } from 'node:fs/promises'
|
import { readFile, stat } from 'node:fs/promises'
|
||||||
import { basename } from 'node:path'
|
import { basename } from 'node:path'
|
||||||
|
|
||||||
import { Adapters, create, getOptions, FileDTO, Note, NoteMeta } from '@cryptgeon/shared'
|
|
||||||
import mime from 'mime'
|
import mime from 'mime'
|
||||||
import { AES, Hex } from 'occulto'
|
import { AES, Hex } from 'occulto'
|
||||||
|
import { Adapters } from '../shared/adapters.js'
|
||||||
|
import { API, FileDTO, Note, NoteMeta } from '../shared/api.js'
|
||||||
|
|
||||||
export type UploadOptions = Pick<Note, 'views' | 'expiration'> & { password?: string }
|
export type UploadOptions = Pick<Note, 'views' | 'expiration'> & { password?: string }
|
||||||
|
|
||||||
@@ -38,8 +39,8 @@ export async function upload(input: string | string[], options: UploadOptions):
|
|||||||
|
|
||||||
// Create the actual note and upload it.
|
// Create the actual note and upload it.
|
||||||
const note: Note = { ...noteOptions, contents, meta: { type, derivation: derived?.[1] } }
|
const note: Note = { ...noteOptions, contents, meta: { type, derivation: derived?.[1] } }
|
||||||
const result = await create(note)
|
const result = await API.create(note)
|
||||||
let url = `${getOptions().server}/note/${result.id}`
|
let url = `${API.getOptions().server}/note/${result.id}`
|
||||||
if (!derived) url += `#${Hex.encode(key)}`
|
if (!derived) url += `#${Hex.encode(key)}`
|
||||||
return url
|
return url
|
||||||
}
|
}
|
@@ -1,14 +1,14 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { Argument, Option, program } from '@commander-js/extra-typings'
|
import { Argument, Option, program } from '@commander-js/extra-typings'
|
||||||
import { setOptions, status } from '@cryptgeon/shared'
|
|
||||||
import prettyBytes from 'pretty-bytes'
|
import prettyBytes from 'pretty-bytes'
|
||||||
|
|
||||||
import { download } from './download.js'
|
import { download } from './actions/download.js'
|
||||||
import { parseFile, parseNumber } from './parsers.js'
|
import { upload } from './actions/upload.js'
|
||||||
import { getStdin } from './stdin.js'
|
import { API } from './shared/api.js'
|
||||||
import { upload } from './upload.js'
|
import { parseFile, parseNumber } from './utils/parsers.js'
|
||||||
import { checkConstrains, exit } from './utils.js'
|
import { getStdin } from './utils/stdin.js'
|
||||||
|
import { checkConstrains, exit } from './utils/utils.js'
|
||||||
|
|
||||||
const defaultServer = process.env['CRYPTGEON_SERVER'] || 'https://cryptgeon.org'
|
const defaultServer = process.env['CRYPTGEON_SERVER'] || 'https://cryptgeon.org'
|
||||||
const server = new Option('-s --server <url>', 'the cryptgeon server to use').default(defaultServer)
|
const server = new Option('-s --server <url>', 'the cryptgeon server to use').default(defaultServer)
|
||||||
@@ -33,8 +33,8 @@ program
|
|||||||
.description('show information about the server')
|
.description('show information about the server')
|
||||||
.addOption(server)
|
.addOption(server)
|
||||||
.action(async (options) => {
|
.action(async (options) => {
|
||||||
setOptions({ server: options.server })
|
API.setOptions({ server: options.server })
|
||||||
const response = await status()
|
const response = await API.status()
|
||||||
const formatted = {
|
const formatted = {
|
||||||
...response,
|
...response,
|
||||||
max_size: prettyBytes(response.max_size),
|
max_size: prettyBytes(response.max_size),
|
||||||
@@ -54,7 +54,7 @@ send
|
|||||||
.addOption(minutes)
|
.addOption(minutes)
|
||||||
.addOption(password)
|
.addOption(password)
|
||||||
.action(async (files, options) => {
|
.action(async (files, options) => {
|
||||||
setOptions({ server: options.server })
|
API.setOptions({ server: options.server })
|
||||||
await checkConstrains(options)
|
await checkConstrains(options)
|
||||||
options.password ||= await getStdin()
|
options.password ||= await getStdin()
|
||||||
try {
|
try {
|
||||||
@@ -72,7 +72,7 @@ send
|
|||||||
.addOption(minutes)
|
.addOption(minutes)
|
||||||
.addOption(password)
|
.addOption(password)
|
||||||
.action(async (text, options) => {
|
.action(async (text, options) => {
|
||||||
setOptions({ server: options.server })
|
API.setOptions({ server: options.server })
|
||||||
await checkConstrains(options)
|
await checkConstrains(options)
|
||||||
options.password ||= await getStdin()
|
options.password ||= await getStdin()
|
||||||
try {
|
try {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
export * from '@cryptgeon/shared'
|
export * from './actions/download.js'
|
||||||
export * from './download.js'
|
export * from './actions/upload.js'
|
||||||
export * from './upload.js'
|
export * from './shared/adapters.js'
|
||||||
export * from './utils.js'
|
export * from './shared/api.js'
|
||||||
|
@@ -39,15 +39,15 @@ export let client: ClientOptions = {
|
|||||||
server: '',
|
server: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setOptions(options: Partial<ClientOptions>) {
|
function setOptions(options: Partial<ClientOptions>) {
|
||||||
client = { ...client, ...options }
|
client = { ...client, ...options }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOptions(): ClientOptions {
|
function getOptions(): ClientOptions {
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function call(options: CallOptions) {
|
async function call(options: CallOptions) {
|
||||||
const url = client.server + '/api/' + options.url
|
const url = client.server + '/api/' + options.url
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: options.method,
|
method: options.method,
|
||||||
@@ -65,7 +65,7 @@ export async function call(options: CallOptions) {
|
|||||||
return response.json()
|
return response.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(note: Note) {
|
async function create(note: Note) {
|
||||||
const { meta, ...rest } = note
|
const { meta, ...rest } = note
|
||||||
const body: NoteCreate = {
|
const body: NoteCreate = {
|
||||||
...rest,
|
...rest,
|
||||||
@@ -79,7 +79,7 @@ export async function create(note: Note) {
|
|||||||
return data as { id: string }
|
return data as { id: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get(id: string): Promise<NotePublic> {
|
async function get(id: string): Promise<NotePublic> {
|
||||||
const data = await call({
|
const data = await call({
|
||||||
url: `notes/${id}`,
|
url: `notes/${id}`,
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -93,7 +93,7 @@ export async function get(id: string): Promise<NotePublic> {
|
|||||||
return note
|
return note
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function info(id: string): Promise<NoteInfo> {
|
async function info(id: string): Promise<NoteInfo> {
|
||||||
const data = await call({
|
const data = await call({
|
||||||
url: `notes/${id}`,
|
url: `notes/${id}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -112,6 +112,7 @@ export type Status = {
|
|||||||
max_views: number
|
max_views: number
|
||||||
max_expiration: number
|
max_expiration: number
|
||||||
allow_advanced: boolean
|
allow_advanced: boolean
|
||||||
|
allow_files: boolean
|
||||||
theme_image: string
|
theme_image: string
|
||||||
theme_text: string
|
theme_text: string
|
||||||
theme_favicon: string
|
theme_favicon: string
|
||||||
@@ -119,10 +120,19 @@ export type Status = {
|
|||||||
theme_new_note_notice: boolean
|
theme_new_note_notice: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function status() {
|
async function status() {
|
||||||
const data = await call({
|
const data = await call({
|
||||||
url: 'status/',
|
url: 'status/',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
return data as Status
|
return data as Status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const API = {
|
||||||
|
setOptions,
|
||||||
|
getOptions,
|
||||||
|
create,
|
||||||
|
get,
|
||||||
|
info,
|
||||||
|
status,
|
||||||
|
}
|
@@ -21,7 +21,7 @@ export function parseURL(value: string, _: URL): URL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parseNumber(value: string, _: number): number {
|
export function parseNumber(value: string, _: number): number {
|
||||||
const n = parseInt(value, 10)
|
const n = Number.parseInt(value, 10)
|
||||||
if (isNaN(n)) throw new InvalidOptionArgumentError('invalid number')
|
if (Number.isNaN(n)) throw new InvalidOptionArgumentError('invalid number')
|
||||||
return n
|
return n
|
||||||
}
|
}
|
@@ -18,6 +18,7 @@ export function getStdin(timeout: number = 10): Promise<string> {
|
|||||||
resolve('')
|
resolve('')
|
||||||
}, timeout)
|
}, timeout)
|
||||||
|
|
||||||
|
process.stdin.on('error', reject)
|
||||||
process.stdin.on('data', dataHandler)
|
process.stdin.on('data', dataHandler)
|
||||||
process.stdin.on('end', endHandler)
|
process.stdin.on('end', endHandler)
|
||||||
})
|
})
|
@@ -1,5 +1,5 @@
|
|||||||
import { status } from '@cryptgeon/shared'
|
|
||||||
import { exit as exitNode } from 'node:process'
|
import { exit as exitNode } from 'node:process'
|
||||||
|
import { API } from '../shared/api.js'
|
||||||
|
|
||||||
export function exit(message: string) {
|
export function exit(message: string) {
|
||||||
console.error(message)
|
console.error(message)
|
||||||
@@ -11,7 +11,7 @@ export async function checkConstrains(constrains: { views?: number; minutes?: nu
|
|||||||
if (views && minutes) exit('cannot set view and minutes constrains simultaneously')
|
if (views && minutes) exit('cannot set view and minutes constrains simultaneously')
|
||||||
if (!views && !minutes) constrains.views = 1
|
if (!views && !minutes) constrains.views = 1
|
||||||
|
|
||||||
const response = await status()
|
const response = await API.status()
|
||||||
if (views && views > response.max_views)
|
if (views && views > response.max_views)
|
||||||
exit(`Only a maximum of ${response.max_views} views allowed. ${views} given.`)
|
exit(`Only a maximum of ${response.max_views} views allowed. ${views} given.`)
|
||||||
if (minutes && minutes > response.max_expiration)
|
if (minutes && minutes > response.max_expiration)
|
@@ -2,7 +2,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2022",
|
"target": "es2022",
|
||||||
"module": "es2022",
|
"module": "es2022",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "Bundler",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"emitDeclarationOnly": true,
|
"emitDeclarationOnly": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
"new_note": "new note",
|
"new_note": "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.)",
|
"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": {
|
"errors": {
|
||||||
"note_to_big": "could not create note. note is to big",
|
"note_to_big": "could not create note. note is too big",
|
||||||
"note_error": "could not create note. please try again.",
|
"note_error": "could not create note. please try again.",
|
||||||
"max": "max: {n}",
|
"max": "max: {n}",
|
||||||
"empty_content": "note is empty."
|
"empty_content": "note is empty."
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
"decryption_failed": "wrong password. could not decipher. probably a broken link. note was destroyed.",
|
"decryption_failed": "wrong password. could not decipher. probably a broken link. note was destroyed.",
|
||||||
"unsupported_type": "unsupported note type."
|
"unsupported_type": "unsupported note type."
|
||||||
},
|
},
|
||||||
"explanation": "click below to show and delete the note if the counter has reached it's limit",
|
"explanation": "click below to show and delete the note if the counter has reached its limit",
|
||||||
"show_note": "show note",
|
"show_note": "show note",
|
||||||
"warning_will_not_see_again": "you will <b>not</b> get the chance to see the note again.",
|
"warning_will_not_see_again": "you will <b>not</b> get the chance to see the note again.",
|
||||||
"download_all": "download all",
|
"download_all": "download all",
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
"@sveltejs/adapter-static": "^3.0.1",
|
"@sveltejs/adapter-static": "^3.0.1",
|
||||||
"@sveltejs/kit": "^2.5.2",
|
"@sveltejs/kit": "^2.5.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
||||||
"@types/file-saver": "^2.0.7",
|
|
||||||
"@zerodevx/svelte-toast": "^0.9.5",
|
"@zerodevx/svelte-toast": "^0.9.5",
|
||||||
"adm-zip": "^0.5.10",
|
"adm-zip": "^0.5.10",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
@@ -29,11 +28,9 @@
|
|||||||
"vite": "^5.1.7"
|
"vite": "^5.1.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cryptgeon/shared": "workspace:*",
|
"cryptgeon": "workspace:*",
|
||||||
"@fontsource/fira-mono": "^5.0.8",
|
"@fontsource/fira-mono": "^5.0.8",
|
||||||
"copy-to-clipboard": "^3.3.3",
|
"occulto": "^2.0.6",
|
||||||
"file-saver": "^2.0.5",
|
|
||||||
"occulto": "^2.0.3",
|
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
"qrious": "^4.0.2"
|
"qrious": "^4.0.2"
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { status as getStatus, type Status } from '@cryptgeon/shared'
|
import { API, type Status } from 'cryptgeon/shared'
|
||||||
import { writable } from 'svelte/store'
|
import { writable } from 'svelte/store'
|
||||||
|
|
||||||
export const status = writable<null | Status>(null)
|
export const status = writable<null | Status>(null)
|
||||||
|
|
||||||
export async function init() {
|
export async function init() {
|
||||||
status.set(await getStatus())
|
status.set(await API.status())
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { toast, type SvelteToastOptions } from '@zerodevx/svelte-toast'
|
import { toast } from '@zerodevx/svelte-toast'
|
||||||
|
|
||||||
export enum NotifyType {
|
export enum NotifyType {
|
||||||
Success = 'success',
|
Success = 'success',
|
||||||
Error = 'error',
|
Error = 'error',
|
||||||
}
|
}
|
||||||
|
|
||||||
const themeMapping: Record<NotifyType, SvelteToastOptions['theme']> = {
|
const themeMapping: Record<NotifyType, Record<string, string>> = {
|
||||||
[NotifyType.Success]: {
|
[NotifyType.Success]: {
|
||||||
'--toastBackground': 'var(--ui-clr-primary)',
|
'--toastBackground': 'var(--ui-clr-primary)',
|
||||||
'--toastBarBackground': 'var(--ui-clr-primary-alt)',
|
'--toastBarBackground': 'var(--ui-clr-primary-alt)',
|
||||||
@@ -17,7 +17,7 @@ const themeMapping: Record<NotifyType, SvelteToastOptions['theme']> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function notifyFN(message: string, type: NotifyType = NotifyType.Success) {
|
function notifyFN(message: string, type: NotifyType = NotifyType.Success) {
|
||||||
const options: SvelteToastOptions = {
|
const options = {
|
||||||
duration: 5_000,
|
duration: 5_000,
|
||||||
theme: {
|
theme: {
|
||||||
...themeMapping[type],
|
...themeMapping[type],
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
import { status } from '$lib/stores/status'
|
import { status } from '$lib/stores/status'
|
||||||
import Switch from '$lib/ui/Switch.svelte'
|
import Switch from '$lib/ui/Switch.svelte'
|
||||||
import TextInput from '$lib/ui/TextInput.svelte'
|
import TextInput from '$lib/ui/TextInput.svelte'
|
||||||
import type { Note } from '@cryptgeon/shared'
|
import type { Note } from 'cryptgeon/shared'
|
||||||
|
|
||||||
export let note: Note
|
export let note: Note
|
||||||
export let timeExpiration = false
|
export let timeExpiration = false
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import Button from '$lib/ui/Button.svelte'
|
import Button from '$lib/ui/Button.svelte'
|
||||||
import MaxSize from '$lib/ui/MaxSize.svelte'
|
import MaxSize from '$lib/ui/MaxSize.svelte'
|
||||||
import type { FileDTO } from '@cryptgeon/shared'
|
import type { FileDTO } from 'cryptgeon/shared'
|
||||||
|
|
||||||
export let label: string = ''
|
export let label: string = ''
|
||||||
export let files: FileDTO[] = []
|
export let files: FileDTO[] = []
|
||||||
|
@@ -1,16 +1,26 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export type DecryptedNote = Omit<NotePublic, 'contents'> & { contents: any }
|
export type DecryptedNote = Omit<NotePublic, 'contents'> & { contents: any }
|
||||||
|
|
||||||
|
function saveAs(file: File) {
|
||||||
|
const url = window.URL.createObjectURL(file)
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.style.display = 'none'
|
||||||
|
a.href = url
|
||||||
|
a.download = file.name
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
a.remove()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import pkg from 'file-saver'
|
|
||||||
const { saveAs } = pkg
|
|
||||||
import prettyBytes from 'pretty-bytes'
|
import prettyBytes from 'pretty-bytes'
|
||||||
import { t } from 'svelte-intl-precompile'
|
import { t } from 'svelte-intl-precompile'
|
||||||
|
|
||||||
import Button from '$lib/ui/Button.svelte'
|
import Button from '$lib/ui/Button.svelte'
|
||||||
import { copy } from '$lib/utils'
|
import { copy } from '$lib/utils'
|
||||||
import type { FileDTO, NotePublic } from '@cryptgeon/shared'
|
import type { FileDTO, NotePublic } from 'cryptgeon/shared'
|
||||||
|
|
||||||
export let note: DecryptedNote
|
export let note: DecryptedNote
|
||||||
|
|
||||||
|
@@ -39,13 +39,23 @@
|
|||||||
<input bind:value {...$$restProps} class:valid={valid === true} />
|
<input bind:value {...$$restProps} class:valid={valid === true} />
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
{#if isPassword}
|
{#if isPassword}
|
||||||
<Icon class="icon" icon={hidden ? 'eye' : 'eye-off'} on:click={toggle} />
|
<Icon
|
||||||
|
disabled={$$restProps.disabled}
|
||||||
|
class="icon"
|
||||||
|
icon={hidden ? 'eye' : 'eye-off'}
|
||||||
|
on:click={toggle}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if random}
|
{#if random}
|
||||||
<Icon class="icon" icon="dice" on:click={randomFN} />
|
<Icon disabled={$$restProps.disabled} class="icon" icon="dice" on:click={randomFN} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if copy}
|
{#if copy}
|
||||||
<Icon class="icon" icon="copy" on:click={() => copyFN(value.toString())} />
|
<Icon
|
||||||
|
disabled={$$restProps.disabled}
|
||||||
|
class="icon"
|
||||||
|
icon="copy"
|
||||||
|
on:click={() => copyFN(value.toString())}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import copyToClipboard from 'copy-to-clipboard'
|
|
||||||
import { t } from 'svelte-intl-precompile'
|
import { t } from 'svelte-intl-precompile'
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
|
|
||||||
import { notify } from './toast'
|
import { notify } from './toast'
|
||||||
|
|
||||||
export function copy(value: string) {
|
export function copy(value: string) {
|
||||||
copyToClipboard(value)
|
window.navigator.clipboard.writeText(value)
|
||||||
const msg = get(t)('common.copied_to_clipboard')
|
const msg = get(t)('common.copied_to_clipboard')
|
||||||
notify.success(msg)
|
notify.success(msg)
|
||||||
}
|
}
|
||||||
|
@@ -13,8 +13,7 @@
|
|||||||
import Result, { type NoteResult } from '$lib/ui/NoteResult.svelte'
|
import Result, { type NoteResult } from '$lib/ui/NoteResult.svelte'
|
||||||
import Switch from '$lib/ui/Switch.svelte'
|
import Switch from '$lib/ui/Switch.svelte'
|
||||||
import TextArea from '$lib/ui/TextArea.svelte'
|
import TextArea from '$lib/ui/TextArea.svelte'
|
||||||
import type { FileDTO, Note } from '@cryptgeon/shared'
|
import { Adapters, API, PayloadToLargeError, type FileDTO, type Note } from 'cryptgeon/shared'
|
||||||
import { Adapters, PayloadToLargeError, create } from '@cryptgeon/shared'
|
|
||||||
|
|
||||||
let note: Note = {
|
let note: Note = {
|
||||||
contents: '',
|
contents: '',
|
||||||
@@ -77,7 +76,7 @@
|
|||||||
else data.views = parseInt(note.views as any)
|
else data.views = parseInt(note.views as any)
|
||||||
|
|
||||||
loading = $t('common.uploading')
|
loading = $t('common.uploading')
|
||||||
const response = await create(data)
|
const response = await API.create(data)
|
||||||
result = {
|
result = {
|
||||||
id: response.id,
|
id: response.id,
|
||||||
password: customPassword ? undefined : Hex.encode(key),
|
password: customPassword ? undefined : Hex.encode(key),
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
import Loader from '$lib/ui/Loader.svelte'
|
import Loader from '$lib/ui/Loader.svelte'
|
||||||
import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte'
|
import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte'
|
||||||
import TextInput from '$lib/ui/TextInput.svelte'
|
import TextInput from '$lib/ui/TextInput.svelte'
|
||||||
import { Adapters, get, info, type NoteMeta } from '@cryptgeon/shared'
|
import { Adapters, API, type NoteMeta } from 'cryptgeon/shared'
|
||||||
import type { PageData } from './$types'
|
import type { PageData } from './$types'
|
||||||
|
|
||||||
export let data: PageData
|
export let data: PageData
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
try {
|
try {
|
||||||
loading = $t('common.loading')
|
loading = $t('common.loading')
|
||||||
password = window.location.hash.slice(1)
|
password = window.location.hash.slice(1)
|
||||||
const note = await info(id)
|
const note = await API.info(id)
|
||||||
meta = note.meta
|
meta = note.meta
|
||||||
exists = true
|
exists = true
|
||||||
} catch {
|
} catch {
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
// Load note
|
// Load note
|
||||||
error = null
|
error = null
|
||||||
loading = $t('common.downloading')
|
loading = $t('common.downloading')
|
||||||
const data = await get(id)
|
const data = await API.get(id)
|
||||||
loading = $t('common.decrypting')
|
loading = $t('common.decrypting')
|
||||||
const derived = meta?.derivation && (await AES.derive(password!, meta.derivation))
|
const derived = meta?.derivation && (await AES.derive(password!, meta.derivation))
|
||||||
const key = derived ? derived[0] : Hex.decode(password!)
|
const key = derived ? derived[0] : Hex.decode(password!)
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"private": true,
|
|
||||||
"name": "@cryptgeon/shared",
|
|
||||||
"type": "module",
|
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"import": "./dist/index.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"dev": "tsc -w",
|
|
||||||
"build": "tsc"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"typescript": "^5.3.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"occulto": "^2.0.3"
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"incremental": true,
|
|
||||||
"composite": true,
|
|
||||||
"target": "es2022",
|
|
||||||
"module": "es2022",
|
|
||||||
"rootDir": "./src",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"outDir": "./dist",
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
@@ -4,16 +4,17 @@ const config: PlaywrightTestConfig = {
|
|||||||
use: {
|
use: {
|
||||||
video: 'retain-on-failure',
|
video: 'retain-on-failure',
|
||||||
baseURL: 'http://localhost:3000',
|
baseURL: 'http://localhost:3000',
|
||||||
actionTimeout: 10_000,
|
actionTimeout: 30_000,
|
||||||
},
|
},
|
||||||
|
|
||||||
outputDir: './test-results',
|
outputDir: './test-results',
|
||||||
testDir: './test',
|
testDir: './test',
|
||||||
timeout: 30_000,
|
timeout: 30_000,
|
||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
|
retries: 2,
|
||||||
|
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'docker compose -f docker-compose.dev.yaml up',
|
command: 'pnpm run docker:up',
|
||||||
port: 3000,
|
port: 3000,
|
||||||
reuseExistingServer: true,
|
reuseExistingServer: true,
|
||||||
},
|
},
|
||||||
|
103
pnpm-lock.yaml
generated
103
pnpm-lock.yaml
generated
@@ -26,23 +26,20 @@ importers:
|
|||||||
packages/cli:
|
packages/cli:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@commander-js/extra-typings':
|
'@commander-js/extra-typings':
|
||||||
specifier: ^12.0.1
|
specifier: ^12.1.0
|
||||||
version: 12.0.1(commander@12.0.0)
|
version: 12.1.0(commander@12.1.0)
|
||||||
'@cryptgeon/shared':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../shared
|
|
||||||
'@types/inquirer':
|
'@types/inquirer':
|
||||||
specifier: ^9.0.7
|
specifier: ^9.0.7
|
||||||
version: 9.0.7
|
version: 9.0.7
|
||||||
'@types/mime':
|
'@types/mime':
|
||||||
specifier: ^3.0.4
|
specifier: ^4.0.0
|
||||||
version: 3.0.4
|
version: 4.0.0
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^20.11.24
|
specifier: ^20.11.24
|
||||||
version: 20.11.24
|
version: 20.11.24
|
||||||
commander:
|
commander:
|
||||||
specifier: ^12.0.0
|
specifier: ^12.1.0
|
||||||
version: 12.0.0
|
version: 12.1.0
|
||||||
inquirer:
|
inquirer:
|
||||||
specifier: ^9.2.15
|
specifier: ^9.2.15
|
||||||
version: 9.2.15
|
version: 9.2.15
|
||||||
@@ -50,8 +47,8 @@ importers:
|
|||||||
specifier: ^4.0.1
|
specifier: ^4.0.1
|
||||||
version: 4.0.1
|
version: 4.0.1
|
||||||
occulto:
|
occulto:
|
||||||
specifier: ^2.0.3
|
specifier: ^2.0.6
|
||||||
version: 2.0.3
|
version: 2.0.6
|
||||||
pretty-bytes:
|
pretty-bytes:
|
||||||
specifier: ^6.1.1
|
specifier: ^6.1.1
|
||||||
version: 6.1.1
|
version: 6.1.1
|
||||||
@@ -64,21 +61,15 @@ importers:
|
|||||||
|
|
||||||
packages/frontend:
|
packages/frontend:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@cryptgeon/shared':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../shared
|
|
||||||
'@fontsource/fira-mono':
|
'@fontsource/fira-mono':
|
||||||
specifier: ^5.0.8
|
specifier: ^5.0.8
|
||||||
version: 5.0.8
|
version: 5.0.8
|
||||||
copy-to-clipboard:
|
cryptgeon:
|
||||||
specifier: ^3.3.3
|
specifier: workspace:*
|
||||||
version: 3.3.3
|
version: link:../cli
|
||||||
file-saver:
|
|
||||||
specifier: ^2.0.5
|
|
||||||
version: 2.0.5
|
|
||||||
occulto:
|
occulto:
|
||||||
specifier: ^2.0.3
|
specifier: ^2.0.6
|
||||||
version: 2.0.3
|
version: 2.0.6
|
||||||
pretty-bytes:
|
pretty-bytes:
|
||||||
specifier: ^6.1.1
|
specifier: ^6.1.1
|
||||||
version: 6.1.1
|
version: 6.1.1
|
||||||
@@ -98,9 +89,6 @@ importers:
|
|||||||
'@sveltejs/vite-plugin-svelte':
|
'@sveltejs/vite-plugin-svelte':
|
||||||
specifier: ^3.0.2
|
specifier: ^3.0.2
|
||||||
version: 3.0.2(svelte@4.2.12)(vite@5.1.7(@types/node@22.5.0))
|
version: 3.0.2(svelte@4.2.12)(vite@5.1.7(@types/node@22.5.0))
|
||||||
'@types/file-saver':
|
|
||||||
specifier: ^2.0.7
|
|
||||||
version: 2.0.7
|
|
||||||
'@zerodevx/svelte-toast':
|
'@zerodevx/svelte-toast':
|
||||||
specifier: ^0.9.5
|
specifier: ^0.9.5
|
||||||
version: 0.9.5(svelte@4.2.12)
|
version: 0.9.5(svelte@4.2.12)
|
||||||
@@ -135,16 +123,6 @@ importers:
|
|||||||
specifier: ^1.18.1
|
specifier: ^1.18.1
|
||||||
version: 1.18.1
|
version: 1.18.1
|
||||||
|
|
||||||
packages/shared:
|
|
||||||
dependencies:
|
|
||||||
occulto:
|
|
||||||
specifier: ^2.0.3
|
|
||||||
version: 2.0.3
|
|
||||||
devDependencies:
|
|
||||||
typescript:
|
|
||||||
specifier: ^5.3.3
|
|
||||||
version: 5.3.3
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
'@ampproject/remapping@2.3.0':
|
'@ampproject/remapping@2.3.0':
|
||||||
@@ -242,10 +220,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
|
resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
'@commander-js/extra-typings@12.0.1':
|
'@commander-js/extra-typings@12.1.0':
|
||||||
resolution: {integrity: sha512-OvkMobb1eMqOCuJdbuSin/KJkkZr7n24/UNV+Lcz/0Dhepf3r2p9PaGwpRpAWej7A+gQnny4h8mGhpFl4giKkg==}
|
resolution: {integrity: sha512-wf/lwQvWAA0goIghcb91dQYpkLBcyhOhQNqG/VgWhnKzgt+UOMvra7EX/2fv70arm5RW+PUHoQHHDa6/p77Eqg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
commander: ~12.0.0
|
commander: ~12.1.0
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.19.12':
|
'@esbuild/aix-ppc64@0.19.12':
|
||||||
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
|
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
|
||||||
@@ -778,14 +756,12 @@ packages:
|
|||||||
'@types/estree@1.0.5':
|
'@types/estree@1.0.5':
|
||||||
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
||||||
|
|
||||||
'@types/file-saver@2.0.7':
|
|
||||||
resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==}
|
|
||||||
|
|
||||||
'@types/inquirer@9.0.7':
|
'@types/inquirer@9.0.7':
|
||||||
resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==}
|
resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==}
|
||||||
|
|
||||||
'@types/mime@3.0.4':
|
'@types/mime@4.0.0':
|
||||||
resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==}
|
resolution: {integrity: sha512-5eEkJZ/BLvTE3vXGKkWlyTSUVZuzj23Wj8PoyOq2lt5I3CYbiLBOPb3XmCW6QcuOibIUE6emHXHt9E/F/rCa6w==}
|
||||||
|
deprecated: This is a stub types definition. mime provides its own type definitions, so you do not need this installed.
|
||||||
|
|
||||||
'@types/node@20.11.24':
|
'@types/node@20.11.24':
|
||||||
resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==}
|
resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==}
|
||||||
@@ -980,8 +956,8 @@ packages:
|
|||||||
color-name@1.1.4:
|
color-name@1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
commander@12.0.0:
|
commander@12.1.0:
|
||||||
resolution: {integrity: sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==}
|
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
commander@4.1.1:
|
commander@4.1.1:
|
||||||
@@ -1002,9 +978,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
copy-to-clipboard@3.3.3:
|
|
||||||
resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
|
|
||||||
|
|
||||||
cross-spawn@6.0.5:
|
cross-spawn@6.0.5:
|
||||||
resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
|
resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
|
||||||
engines: {node: '>=4.8'}
|
engines: {node: '>=4.8'}
|
||||||
@@ -1153,9 +1126,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
|
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
file-saver@2.0.5:
|
|
||||||
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
|
||||||
|
|
||||||
fill-range@7.0.1:
|
fill-range@7.0.1:
|
||||||
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -1618,9 +1588,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
|
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
occulto@2.0.3:
|
occulto@2.0.6:
|
||||||
resolution: {integrity: sha512-rLXDNqsxM3Gusp4cn8QsveeKsmX36SbnIUcUc3mSeM88pGsNY5Tn6VnWItw/7zST01z9gtaZWQFk0F1L53jMCQ==}
|
resolution: {integrity: sha512-oaCwtnQjr+fTfFVfSPEDC5rh+L13OcEJ6uQzhmG8PlLxxYn2MxTDPmoCUVHKD7rUxwxCpH7/N4hbTu5U4mqZag==}
|
||||||
engines: {node: '>=16', npm: please-use-pnpm, pnpm: '>=8', yarn: please-use-pnpm}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
once@1.4.0:
|
once@1.4.0:
|
||||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||||
@@ -2079,9 +2049,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
|
|
||||||
toggle-selection@1.0.6:
|
|
||||||
resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
|
|
||||||
|
|
||||||
totalist@3.0.1:
|
totalist@3.0.1:
|
||||||
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -2377,9 +2344,9 @@ snapshots:
|
|||||||
'@babel/helper-validator-identifier': 7.22.20
|
'@babel/helper-validator-identifier': 7.22.20
|
||||||
to-fast-properties: 2.0.0
|
to-fast-properties: 2.0.0
|
||||||
|
|
||||||
'@commander-js/extra-typings@12.0.1(commander@12.0.0)':
|
'@commander-js/extra-typings@12.1.0(commander@12.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
commander: 12.0.0
|
commander: 12.1.0
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.19.12':
|
'@esbuild/aix-ppc64@0.19.12':
|
||||||
optional: true
|
optional: true
|
||||||
@@ -2733,14 +2700,14 @@ snapshots:
|
|||||||
|
|
||||||
'@types/estree@1.0.5': {}
|
'@types/estree@1.0.5': {}
|
||||||
|
|
||||||
'@types/file-saver@2.0.7': {}
|
|
||||||
|
|
||||||
'@types/inquirer@9.0.7':
|
'@types/inquirer@9.0.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/through': 0.0.33
|
'@types/through': 0.0.33
|
||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
|
|
||||||
'@types/mime@3.0.4': {}
|
'@types/mime@4.0.0':
|
||||||
|
dependencies:
|
||||||
|
mime: 4.0.1
|
||||||
|
|
||||||
'@types/node@20.11.24':
|
'@types/node@20.11.24':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2942,7 +2909,7 @@ snapshots:
|
|||||||
|
|
||||||
color-name@1.1.4: {}
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
commander@12.0.0: {}
|
commander@12.1.0: {}
|
||||||
|
|
||||||
commander@4.1.1: {}
|
commander@4.1.1: {}
|
||||||
|
|
||||||
@@ -2954,10 +2921,6 @@ snapshots:
|
|||||||
|
|
||||||
cookie@0.6.0: {}
|
cookie@0.6.0: {}
|
||||||
|
|
||||||
copy-to-clipboard@3.3.3:
|
|
||||||
dependencies:
|
|
||||||
toggle-selection: 1.0.6
|
|
||||||
|
|
||||||
cross-spawn@6.0.5:
|
cross-spawn@6.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
nice-try: 1.0.5
|
nice-try: 1.0.5
|
||||||
@@ -3190,8 +3153,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
escape-string-regexp: 1.0.5
|
escape-string-regexp: 1.0.5
|
||||||
|
|
||||||
file-saver@2.0.5: {}
|
|
||||||
|
|
||||||
fill-range@7.0.1:
|
fill-range@7.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
to-regex-range: 5.0.1
|
to-regex-range: 5.0.1
|
||||||
@@ -3623,7 +3584,7 @@ snapshots:
|
|||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
|
|
||||||
occulto@2.0.3: {}
|
occulto@2.0.6: {}
|
||||||
|
|
||||||
once@1.4.0:
|
once@1.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4117,8 +4078,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
|
|
||||||
toggle-selection@1.0.6: {}
|
|
||||||
|
|
||||||
totalist@3.0.1: {}
|
totalist@3.0.1: {}
|
||||||
|
|
||||||
tr46@1.0.1:
|
tr46@1.0.1:
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import { test } from '@playwright/test'
|
import { test } from '@playwright/test'
|
||||||
import { CLI, checkLinkForDownload, checkLinkForText, createNote, getLinkFromCLI } from '../../utils'
|
import { rm } from 'node:fs/promises'
|
||||||
import { Files, getFileChecksum, rm, tmpFile } from '../../files'
|
import { basename } from 'node:path'
|
||||||
import { basename } from 'path'
|
import { Files, getFileChecksum, tmpFile } from '../../files'
|
||||||
|
import { CLI, checkLinkForDownload, createNoteSuccessfully, getLinkFromCLI } from '../../utils'
|
||||||
|
|
||||||
const text = `Endless prejudice endless play derive joy eternal-return selfish burying. Of decieve play pinnacle faith disgust. Spirit reason salvation burying strong of joy ascetic selfish against merciful sea truth. Ubermensch moral prejudice derive chaos mountains ubermensch justice philosophy justice ultimate joy ultimate transvaluation. Virtues convictions war ascetic eternal-return spirit. Ubermensch transvaluation noble revaluation sexuality intentions salvation endless decrepit hope noble fearful. Justice ideal ultimate snare god joy evil sexuality insofar gains oneself ideal.`
|
const text = `Endless prejudice endless play derive joy eternal-return selfish burying. Of decieve play pinnacle faith disgust. Spirit reason salvation burying strong of joy ascetic selfish against merciful sea truth. Ubermensch moral prejudice derive chaos mountains ubermensch justice philosophy justice ultimate joy ultimate transvaluation. Virtues convictions war ascetic eternal-return spirit. Ubermensch transvaluation noble revaluation sexuality intentions salvation endless decrepit hope noble fearful. Justice ideal ultimate snare god joy evil sexuality insofar gains oneself ideal.`
|
||||||
const password = 'password'
|
const password = 'password'
|
||||||
@@ -30,7 +31,7 @@ test.describe('text @cross', () => {
|
|||||||
test('web to cli', async ({ page }) => {
|
test('web to cli', async ({ page }) => {
|
||||||
const files = [Files.Image]
|
const files = [Files.Image]
|
||||||
const checksum = await getFileChecksum(files[0])
|
const checksum = await getFileChecksum(files[0])
|
||||||
const link = await createNote(page, { files })
|
const link = await createNoteSuccessfully(page, { files })
|
||||||
|
|
||||||
const filename = basename(files[0])
|
const filename = basename(files[0])
|
||||||
await CLI('open', link, '--all')
|
await CLI('open', link, '--all')
|
||||||
@@ -42,7 +43,7 @@ test.describe('text @cross', () => {
|
|||||||
test('web to cli with password', async ({ page }) => {
|
test('web to cli with password', async ({ page }) => {
|
||||||
const files = [Files.Image]
|
const files = [Files.Image]
|
||||||
const checksum = await getFileChecksum(files[0])
|
const checksum = await getFileChecksum(files[0])
|
||||||
const link = await createNote(page, { files, password })
|
const link = await createNoteSuccessfully(page, { files, password })
|
||||||
|
|
||||||
const filename = basename(files[0])
|
const filename = basename(files[0])
|
||||||
await CLI('open', link, '--all', '--password', password)
|
await CLI('open', link, '--all', '--password', password)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { test } from '@playwright/test'
|
import { test } from '@playwright/test'
|
||||||
import { CLI, checkLinkForText, createNote, getLinkFromCLI } from '../../utils'
|
import { CLI, checkLinkForText, createNoteSuccessfully, getLinkFromCLI } from '../../utils'
|
||||||
|
|
||||||
const text = `Endless prejudice endless play derive joy eternal-return selfish burying. Of decieve play pinnacle faith disgust. Spirit reason salvation burying strong of joy ascetic selfish against merciful sea truth. Ubermensch moral prejudice derive chaos mountains ubermensch justice philosophy justice ultimate joy ultimate transvaluation. Virtues convictions war ascetic eternal-return spirit. Ubermensch transvaluation noble revaluation sexuality intentions salvation endless decrepit hope noble fearful. Justice ideal ultimate snare god joy evil sexuality insofar gains oneself ideal.`
|
const text = `Endless prejudice endless play derive joy eternal-return selfish burying. Of decieve play pinnacle faith disgust. Spirit reason salvation burying strong of joy ascetic selfish against merciful sea truth. Ubermensch moral prejudice derive chaos mountains ubermensch justice philosophy justice ultimate joy ultimate transvaluation. Virtues convictions war ascetic eternal-return spirit. Ubermensch transvaluation noble revaluation sexuality intentions salvation endless decrepit hope noble fearful. Justice ideal ultimate snare god joy evil sexuality insofar gains oneself ideal.`
|
||||||
const password = 'password'
|
const password = 'password'
|
||||||
@@ -13,7 +13,7 @@ test.describe('text @cross', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('web to cli', async ({ page }) => {
|
test('web to cli', async ({ page }) => {
|
||||||
const link = await createNote(page, { text })
|
const link = await createNoteSuccessfully(page, { text })
|
||||||
const retrieved = await CLI('open', link)
|
const retrieved = await CLI('open', link)
|
||||||
test.expect(retrieved.stdout.trim()).toBe(text)
|
test.expect(retrieved.stdout.trim()).toBe(text)
|
||||||
})
|
})
|
||||||
@@ -25,7 +25,7 @@ test.describe('text @cross', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('web to cli with password', async ({ page }) => {
|
test('web to cli with password', async ({ page }) => {
|
||||||
const link = await createNote(page, { text, password })
|
const link = await createNoteSuccessfully(page, { text, password })
|
||||||
const retrieved = await CLI('open', link, '--password', password)
|
const retrieved = await CLI('open', link, '--password', password)
|
||||||
test.expect(retrieved.stdout.trim()).toBe(text)
|
test.expect(retrieved.stdout.trim()).toBe(text)
|
||||||
})
|
})
|
||||||
|
@@ -6,17 +6,22 @@ import { getFileChecksum } from './files'
|
|||||||
const exec = promisify(execFile)
|
const exec = promisify(execFile)
|
||||||
|
|
||||||
type CreatePage = {
|
type CreatePage = {
|
||||||
text?: string
|
|
||||||
files?: string[]
|
|
||||||
views?: number
|
views?: number
|
||||||
expiration?: number
|
expiration?: number
|
||||||
error?: string
|
error?: string
|
||||||
password?: string
|
password?: string
|
||||||
}
|
} & (
|
||||||
export async function createNote(page: Page, options: CreatePage): Promise<string> {
|
| {
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
files: string[]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
async function createNote(page: Page, options: CreatePage): Promise<void> {
|
||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
|
|
||||||
if (options.text) {
|
if ('text' in options) {
|
||||||
await page.getByTestId('text-field').fill(options.text)
|
await page.getByTestId('text-field').fill(options.text)
|
||||||
} else if (options.files) {
|
} else if (options.files) {
|
||||||
await page.getByTestId('switch-file').click()
|
await page.getByTestId('switch-file').click()
|
||||||
@@ -31,7 +36,8 @@ export async function createNote(page: Page, options: CreatePage): Promise<strin
|
|||||||
if (options.views || options.expiration || options.password) await page.getByTestId('switch-advanced').click()
|
if (options.views || options.expiration || options.password) await page.getByTestId('switch-advanced').click()
|
||||||
if (options.views) {
|
if (options.views) {
|
||||||
await page.getByTestId('field-views').fill(options.views.toString())
|
await page.getByTestId('field-views').fill(options.views.toString())
|
||||||
} else if (options.expiration) {
|
}
|
||||||
|
if (options.expiration) {
|
||||||
await page.getByTestId('switch-advanced-toggle').click()
|
await page.getByTestId('switch-advanced-toggle').click()
|
||||||
await page.getByTestId('field-expiration').fill(options.expiration.toString())
|
await page.getByTestId('field-expiration').fill(options.expiration.toString())
|
||||||
}
|
}
|
||||||
@@ -41,15 +47,18 @@ export async function createNote(page: Page, options: CreatePage): Promise<strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
await page.locator('button:has-text("create")').click()
|
await page.locator('button:has-text("create")').click()
|
||||||
|
}
|
||||||
|
|
||||||
if (options.error) {
|
export async function createNoteSuccessfully(page: Page, options: CreatePage): Promise<string> {
|
||||||
await expect(page.locator('.error-text')).toContainText(options.error, { timeout: 60_000 })
|
await createNote(page, options)
|
||||||
}
|
|
||||||
|
|
||||||
// Return share link
|
|
||||||
return await page.getByTestId('share-link').inputValue()
|
return await page.getByTestId('share-link').inputValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createNoteError(page: Page, options: CreatePage, error: string): Promise<void> {
|
||||||
|
await createNote(page, options)
|
||||||
|
await expect(page.locator('._toastContainer')).toContainText(error)
|
||||||
|
}
|
||||||
|
|
||||||
type CheckLinkBase = {
|
type CheckLinkBase = {
|
||||||
link: string
|
link: string
|
||||||
text: string
|
text: string
|
||||||
@@ -69,7 +78,7 @@ export async function checkLinkForDownload(page: Page, options: CheckLinkBase &
|
|||||||
const path = await download.path()
|
const path = await download.path()
|
||||||
if (!path) throw new Error('Download failed')
|
if (!path) throw new Error('Download failed')
|
||||||
const cs = await getFileChecksum(path)
|
const cs = await getFileChecksum(path)
|
||||||
await expect(cs).toBe(options.checksum)
|
expect(cs).toBe(options.checksum)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkLinkForText(page: Page, options: CheckLinkBase) {
|
export async function checkLinkForText(page: Page, options: CheckLinkBase) {
|
||||||
@@ -78,7 +87,7 @@ export async function checkLinkForText(page: Page, options: CheckLinkBase) {
|
|||||||
if (options.password) await page.getByTestId('show-note-password').fill(options.password)
|
if (options.password) await page.getByTestId('show-note-password').fill(options.password)
|
||||||
await page.getByTestId('show-note-button').click()
|
await page.getByTestId('show-note-button').click()
|
||||||
const text = await page.getByTestId('result').locator('.note').innerText()
|
const text = await page.getByTestId('result').locator('.note').innerText()
|
||||||
await expect(text).toContain(options.text)
|
expect(text).toContain(options.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkLinkDoesNotExist(page: Page, link: string) {
|
export async function checkLinkDoesNotExist(page: Page, link: string) {
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import { test } from '@playwright/test'
|
import { test } from '@playwright/test'
|
||||||
import { Files, getFileChecksum } from '../../files'
|
import { Files, getFileChecksum } from '../../files'
|
||||||
import { checkLinkForDownload, createNote } from '../../utils'
|
import { checkLinkForDownload, createNoteSuccessfully } from '../../utils'
|
||||||
|
|
||||||
test.describe('@web', () => {
|
test.describe('@web', () => {
|
||||||
test('multiple', async ({ page }) => {
|
test('multiple', async ({ page }) => {
|
||||||
const files = [Files.PDF, Files.Image]
|
const files = [Files.PDF, Files.Image]
|
||||||
const checksums = await Promise.all(files.map(getFileChecksum))
|
const checksums = await Promise.all(files.map(getFileChecksum))
|
||||||
const link = await createNote(page, { files, views: 2 })
|
const link = await createNoteSuccessfully(page, { files, views: 2 })
|
||||||
await checkLinkForDownload(page, { link, text: 'image.jpg', checksum: checksums[1] })
|
await checkLinkForDownload(page, { link, text: 'image.jpg', checksum: checksums[1] })
|
||||||
await checkLinkForDownload(page, { link, text: 'AES.pdf', checksum: checksums[0] })
|
await checkLinkForDownload(page, { link, text: 'AES.pdf', checksum: checksums[0] })
|
||||||
})
|
})
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { test } from '@playwright/test'
|
import { test } from '@playwright/test'
|
||||||
import { Files, getFileChecksum } from '../../files'
|
import { Files, getFileChecksum } from '../../files'
|
||||||
import { checkLinkDoesNotExist, checkLinkForDownload, checkLinkForText, createNote } from '../../utils'
|
import { checkLinkDoesNotExist, checkLinkForDownload, checkLinkForText, createNoteSuccessfully } from '../../utils'
|
||||||
|
|
||||||
test.describe('@web', () => {
|
test.describe('@web', () => {
|
||||||
test('simple pdf', async ({ page }) => {
|
test('simple pdf', async ({ page }) => {
|
||||||
const files = [Files.PDF]
|
const files = [Files.PDF]
|
||||||
const link = await createNote(page, { files })
|
const link = await createNoteSuccessfully(page, { files })
|
||||||
await checkLinkForText(page, { link, text: 'AES.pdf' })
|
await checkLinkForText(page, { link, text: 'AES.pdf' })
|
||||||
await checkLinkDoesNotExist(page, link)
|
await checkLinkDoesNotExist(page, link)
|
||||||
})
|
})
|
||||||
@@ -13,21 +13,21 @@ test.describe('@web', () => {
|
|||||||
test('pdf content', async ({ page }) => {
|
test('pdf content', async ({ page }) => {
|
||||||
const files = [Files.PDF]
|
const files = [Files.PDF]
|
||||||
const checksum = await getFileChecksum(files[0])
|
const checksum = await getFileChecksum(files[0])
|
||||||
const link = await createNote(page, { files })
|
const link = await createNoteSuccessfully(page, { files })
|
||||||
await checkLinkForDownload(page, { link, text: 'AES.pdf', checksum })
|
await checkLinkForDownload(page, { link, text: 'AES.pdf', checksum })
|
||||||
})
|
})
|
||||||
|
|
||||||
test('image content', async ({ page }) => {
|
test('image content', async ({ page }) => {
|
||||||
const files = [Files.Image]
|
const files = [Files.Image]
|
||||||
const checksum = await getFileChecksum(files[0])
|
const checksum = await getFileChecksum(files[0])
|
||||||
const link = await createNote(page, { files })
|
const link = await createNoteSuccessfully(page, { files })
|
||||||
await checkLinkForDownload(page, { link, text: 'image.jpg', checksum })
|
await checkLinkForDownload(page, { link, text: 'image.jpg', checksum })
|
||||||
})
|
})
|
||||||
|
|
||||||
test('simple pdf with password', async ({ page }) => {
|
test('simple pdf with password', async ({ page }) => {
|
||||||
const files = [Files.PDF]
|
const files = [Files.PDF]
|
||||||
const password = 'password'
|
const password = 'password'
|
||||||
const link = await createNote(page, { files, password })
|
const link = await createNoteSuccessfully(page, { files, password })
|
||||||
await checkLinkForText(page, { link, text: 'AES.pdf', password })
|
await checkLinkForText(page, { link, text: 'AES.pdf', password })
|
||||||
await checkLinkDoesNotExist(page, link)
|
await checkLinkDoesNotExist(page, link)
|
||||||
})
|
})
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { test } from '@playwright/test'
|
import { test } from '@playwright/test'
|
||||||
import { createNote } from '../../utils'
|
|
||||||
import { Files } from '../../files'
|
import { Files } from '../../files'
|
||||||
|
import { createNoteError } from '../../utils'
|
||||||
|
|
||||||
test.describe('@web', () => {
|
test.describe('@web', () => {
|
||||||
test('to big zip', async ({ page }) => {
|
test.skip('to big zip', async ({ page }) => {
|
||||||
const files = [Files.Zip]
|
const files = [Files.Zip]
|
||||||
await createNote(page, { files, error: 'note is to big' })
|
await createNoteError(page, { files }, 'could not create note. note is too big')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
import { test } from '@playwright/test'
|
import { test } from '@playwright/test'
|
||||||
import { checkLinkDoesNotExist, checkLinkForText, createNote } from '../../utils'
|
import { checkLinkDoesNotExist, checkLinkForText, createNoteSuccessfully } from '../../utils'
|
||||||
|
|
||||||
test.describe('@web', () => {
|
test.describe('@web', () => {
|
||||||
test('1 minute', async ({ page }) => {
|
test('1 minute', async ({ page, browserName }) => {
|
||||||
|
test.skip(browserName === 'webkit')
|
||||||
const text = `Virtues value ascetic revaluation sea dead strong burying.`
|
const text = `Virtues value ascetic revaluation sea dead strong burying.`
|
||||||
const minutes = 1
|
const minutes = 1
|
||||||
const timeout = minutes * 60_000
|
const timeout = minutes * 60_000
|
||||||
test.setTimeout(timeout * 2)
|
test.setTimeout(timeout * 2)
|
||||||
const link = await createNote(page, { text, expiration: minutes })
|
const link = await createNoteSuccessfully(page, { text, expiration: minutes })
|
||||||
await checkLinkForText(page, { link, text })
|
await checkLinkForText(page, { link, text })
|
||||||
await checkLinkForText(page, { link, text })
|
await checkLinkForText(page, { link, text })
|
||||||
await page.waitForTimeout(timeout)
|
await page.waitForTimeout(timeout)
|
||||||
|
@@ -1,17 +1,17 @@
|
|||||||
import { test } from '@playwright/test'
|
import { test } from '@playwright/test'
|
||||||
import { checkLinkForText, createNote } from '../../utils'
|
import { checkLinkForText, createNoteSuccessfully } from '../../utils'
|
||||||
|
|
||||||
test.describe('@web', () => {
|
test.describe('@web', () => {
|
||||||
test('simple', async ({ page }) => {
|
test('simple', async ({ page }) => {
|
||||||
const text = `Endless prejudice endless play derive joy eternal-return selfish burying. Of deceive play pinnacle faith disgust. Spirit reason salvation burying strong of joy ascetic selfish against merciful sea truth. Ubermensch moral prejudice derive chaos mountains ubermensch justice philosophy justice ultimate joy ultimate transvaluation. Virtues convictions war ascetic eternal-return spirit. Ubermensch transvaluation noble revaluation sexuality intentions salvation endless decrepit hope noble fearful. Justice ideal ultimate snare god joy evil sexuality insofar gains oneself ideal.`
|
const text = `Endless prejudice endless play derive joy eternal-return selfish burying. Of deceive play pinnacle faith disgust. Spirit reason salvation burying strong of joy ascetic selfish against merciful sea truth. Ubermensch moral prejudice derive chaos mountains ubermensch justice philosophy justice ultimate joy ultimate transvaluation. Virtues convictions war ascetic eternal-return spirit. Ubermensch transvaluation noble revaluation sexuality intentions salvation endless decrepit hope noble fearful. Justice ideal ultimate snare god joy evil sexuality insofar gains oneself ideal.`
|
||||||
const link = await createNote(page, { text })
|
const link = await createNoteSuccessfully(page, { text })
|
||||||
await checkLinkForText(page, { link, text })
|
await checkLinkForText(page, { link, text })
|
||||||
})
|
})
|
||||||
|
|
||||||
test('simple with password', async ({ page }) => {
|
test('simple with password', async ({ page }) => {
|
||||||
const text = 'Foo bar'
|
const text = 'Foo bar'
|
||||||
const password = '123'
|
const password = '123'
|
||||||
const shareLink = await createNote(page, { text, password })
|
const shareLink = await createNoteSuccessfully(page, { text, password })
|
||||||
await checkLinkForText(page, { link: shareLink, text, password })
|
await checkLinkForText(page, { link: shareLink, text, password })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@@ -1,17 +1,17 @@
|
|||||||
import { test } from '@playwright/test'
|
import { test } from '@playwright/test'
|
||||||
import { checkLinkDoesNotExist, checkLinkForText, createNote } from '../../utils'
|
import { checkLinkDoesNotExist, checkLinkForText, createNoteSuccessfully } from '../../utils'
|
||||||
|
|
||||||
test.describe('@web', () => {
|
test.describe('@web', () => {
|
||||||
test('only shown once', async ({ page }) => {
|
test('only shown once', async ({ page }) => {
|
||||||
const text = `Christian victorious reason suicide dead. Right ultimate gains god hope truth burying selfish society joy. Ultimate.`
|
const text = `Christian victorious reason suicide dead. Right ultimate gains god hope truth burying selfish society joy. Ultimate.`
|
||||||
const link = await createNote(page, { text })
|
const link = await createNoteSuccessfully(page, { text })
|
||||||
await checkLinkForText(page, { link, text })
|
await checkLinkForText(page, { link, text })
|
||||||
await checkLinkDoesNotExist(page, link)
|
await checkLinkDoesNotExist(page, link)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('view 3 times', async ({ page }) => {
|
test('view 3 times', async ({ page }) => {
|
||||||
const text = `Justice holiest overcome fearful strong ultimate holiest christianity.`
|
const text = `Justice holiest overcome fearful strong ultimate holiest christianity.`
|
||||||
const link = await createNote(page, { text, views: 3 })
|
const link = await createNoteSuccessfully(page, { text, views: 3 })
|
||||||
await checkLinkForText(page, { link, text })
|
await checkLinkForText(page, { link, text })
|
||||||
await checkLinkForText(page, { link, text })
|
await checkLinkForText(page, { link, text })
|
||||||
await checkLinkForText(page, { link, text })
|
await checkLinkForText(page, { link, text })
|
||||||
|
Reference in New Issue
Block a user