Skip to content
This repository has been archived by the owner on Jun 27, 2024. It is now read-only.

Commit

Permalink
refactor: restructure codebase (#189)
Browse files Browse the repository at this point in the history
* refactor: restructure codebase

* fix test suite

* refactor: remove store

* fmt
  • Loading branch information
boywithkeyboard authored Aug 20, 2023
1 parent 09cd49a commit 29f5fe8
Show file tree
Hide file tree
Showing 14 changed files with 169 additions and 280 deletions.
47 changes: 1 addition & 46 deletions cli/commands/bundle.ts → cli/cmd/bundle/bundle.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,9 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import { parse } from 'https://deno.land/std@0.198.0/flags/mod.ts'
import {
brightGreen,
brightRed,
brightYellow,
gray,
} from 'https://deno.land/std@0.198.0/fmt/colors.ts'
import { ensureFile } from 'https://deno.land/std@0.198.0/fs/ensure_file.ts'
import { join } from 'https://deno.land/std@0.198.0/path/mod.ts'
import byte from 'https://deno.land/x/byte@v3.3.0/byte.ts'
import * as esbuild from 'https://deno.land/x/esbuild@v0.19.2/mod.js'
import { log } from '../utils.ts'

export async function bundleCommand(args: ReturnType<typeof parse>) {
const { _, target, runtime } = args

const input = _[1] && typeof _[1] === 'string' ? _[1] : undefined
const output = _[2] && typeof _[2] === 'string' ? _[2] : undefined

try {
const { outputSize } = await bundle({
input,
output,
// @ts-ignore:
target: typeof target === 'string' ? target : undefined,
// @ts-ignore:
runtime: typeof runtime === 'string' ? runtime : undefined,
})

console.info(
gray(
`file size - ${
outputSize < 1_000_000
? brightGreen(byte(outputSize))
: outputSize < 5_000_000
? brightYellow(byte(outputSize))
: brightRed(byte(outputSize))
}`,
),
)
} catch (err) {
if (err instanceof Error) {
log.error(err.message)
} else {
log.error('something went wrong trying to bundle your app.')
}
}
}

async function bundle({
export async function bundle({
input = './mod.ts',
output = './mod.js',
banner = '// deno-fmt-ignore-file\n// deno-lint-ignore-file',
Expand Down
47 changes: 47 additions & 0 deletions cli/cmd/bundle/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import { parse } from 'https://deno.land/std@0.198.0/flags/mod.ts'
import {
brightGreen,
brightRed,
brightYellow,
gray,
} from 'https://deno.land/std@0.198.0/fmt/colors.ts'
import byte from 'https://deno.land/x/byte@v3.3.0/byte.ts'
import { logError } from '../../utils.ts'
import { bundle } from './bundle.ts'

export async function bundleCommand(args: ReturnType<typeof parse>) {
const { _, target, runtime } = args

const input = _[1] && typeof _[1] === 'string' ? _[1] : undefined
const output = _[2] && typeof _[2] === 'string' ? _[2] : undefined

try {
const { outputSize } = await bundle({
input,
output,
// @ts-ignore:
target: typeof target === 'string' ? target : undefined,
// @ts-ignore:
runtime: typeof runtime === 'string' ? runtime : undefined,
})

console.info(
gray(
`file size - ${
outputSize < 1_000_000
? brightGreen(byte(outputSize))
: outputSize < 5_000_000
? brightYellow(byte(outputSize))
: brightRed(byte(outputSize))
}`,
),
)
} catch (err) {
if (err instanceof Error) {
logError(err.message)
} else {
logError('something went wrong trying to bundle your app.')
}
}
}
File renamed without changes.
14 changes: 14 additions & 0 deletions cli/cmd/random/create_crypto_key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import { encode } from 'https://deno.land/std@0.198.0/encoding/base64.ts'

export async function createCryptoKey() {
const key = await crypto.subtle.generateKey(
{ name: 'AES-GCM', hash: 'SHA-512', length: 256 },
true,
['encrypt', 'decrypt'],
)

const exportedKey = await crypto.subtle.exportKey('raw', key)

return encode(exportedKey)
}
14 changes: 14 additions & 0 deletions cli/cmd/random/create_jwt_secret.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import { encode } from 'https://deno.land/std@0.198.0/encoding/base64.ts'

export async function createJwtSecret() {
const key = await crypto.subtle.generateKey(
{ name: 'HMAC', hash: 'SHA-512' },
true,
['sign', 'verify'],
)

const exportedKey = await crypto.subtle.exportKey('raw', key)

return encode(exportedKey)
}
16 changes: 4 additions & 12 deletions cli/commands/random.ts → cli/cmd/random/mod.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import { encode } from 'https://deno.land/std@0.198.0/encoding/base64.ts'
import { gray, white } from 'https://deno.land/std@0.198.0/fmt/colors.ts'
import { Select } from 'https://deno.land/x/cliffy@v0.25.7/mod.ts'
import * as jwt from '../../x/jwt.ts'
import { createCryptoKey } from './create_crypto_key.ts'
import { createJwtSecret } from './create_jwt_secret.ts'

export async function randomCommand() {
const type: string = await Select.prompt({
Expand All @@ -15,19 +15,11 @@ export async function randomCommand() {

if (type === 'jwt_secret') {
console.info(
gray(`🗝️ ${white(type.replace('_', ' '))} - ${await jwt.createKey()}`),
gray(`🗝️ ${white(type.replace('_', ' '))} - ${await createJwtSecret()}`),
)
} else {
const key = await crypto.subtle.generateKey(
{ name: 'AES-GCM', hash: 'SHA-512', length: 256 },
true,
['encrypt', 'decrypt'],
)

const exportedKey = await crypto.subtle.exportKey('raw', key)

console.info(
gray(`🗝️ ${white(type.replace('_', ' '))} - ${encode(exportedKey)}`),
gray(`🗝️ ${white(type.replace('_', ' '))} - ${await createCryptoKey()}`),
)
}
}
16 changes: 11 additions & 5 deletions cli/commands/serve.ts → cli/cmd/serve/mod.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import { loadSync } from 'https://deno.land/std@0.198.0/dotenv/mod.ts'
import { parse } from 'https://deno.land/std@0.198.0/flags/mod.ts'
import { brightGreen, gray } from 'https://deno.land/std@0.198.0/fmt/colors.ts'
import {
brightGreen,
gray,
white,
} from 'https://deno.land/std@0.198.0/fmt/colors.ts'
import {
keypress,
KeyPressEvent,
} from 'https://deno.land/x/cliffy@v0.25.7/keypress/mod.ts'
import { cheetah } from '../../cheetah.ts'
import { log } from '../utils.ts'
import { cheetah } from '../../../cheetah.ts'
import { logError } from '../../utils.ts'

export async function serveCommand(args: ReturnType<typeof parse>) {
if (typeof args._[1] !== 'string') {
return log.error('please specify an entry point')
return logError('please specify an entry point')
}

loadSync({ export: true })
Expand Down Expand Up @@ -58,7 +62,9 @@ export async function serveCommand(args: ReturnType<typeof parse>) {
childProcess = cmd.spawn()
}

console.info(gray(`${brightGreen('success')} - press CTRL+C to exit`))
console.info(
gray(`${brightGreen('success')} - press ${white('CTRL+C')} to exit`),
)

for await (const e of keypress()) {
const { ctrlKey, key } = e as KeyPressEvent
Expand Down
12 changes: 6 additions & 6 deletions cli/mod.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import { parse } from 'https://deno.land/std@0.198.0/flags/mod.ts'
import { bundleCommand } from './commands/bundle.ts'
import { randomCommand } from './commands/random.ts'
import { newCommand } from './commands/new.ts'
import { serveCommand } from './commands/serve.ts'
import { log } from './utils.ts'
import { bundleCommand } from './cmd/bundle/mod.ts'
import { newCommand } from './cmd/new/mod.ts'
import { randomCommand } from './cmd/random/mod.ts'
import { serveCommand } from './cmd/serve/mod.ts'
import { logError } from './utils.ts'

const args = parse(Deno.args)

Expand All @@ -17,5 +17,5 @@ if (args._[0] === 'bundle') {
} else if (args._[0] === 'serve') {
serveCommand(args)
} else {
log.error('unknown command')
logError('unknown command')
}
6 changes: 2 additions & 4 deletions cli/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import { brightRed, gray } from 'https://deno.land/std@0.198.0/fmt/colors.ts'

export const log = {
error(message: string) {
console.error(gray(`${brightRed('error')} - ${message}`))
},
export function logError(message: string) {
console.error(gray(`${brightRed('error')} - ${message}`))
}
56 changes: 56 additions & 0 deletions crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import { decode } from 'https://deno.land/std@0.198.0/encoding/base64.ts'
import { Context } from './context.ts'

export async function encrypt(c: Context, message: string) {
const key = (c.env('crypto_key') ?? c.env('CRYPTO_KEY')) as string

const iv = crypto.getRandomValues(new Uint8Array(12))
const ivStr = Array.from(iv)
.map((byte) => String.fromCharCode(byte))
.join('')
const alg = { name: 'AES-GCM', iv }
const cryptoKey = await crypto.subtle.importKey(
'raw',
decode(key).buffer,
alg,
true,
['encrypt', 'decrypt'],
)
const cipherBuf = await crypto.subtle.encrypt(
alg,
cryptoKey,
new TextEncoder().encode(message),
)
const cipherArr = Array.from(new Uint8Array(cipherBuf))
const cipherStr = cipherArr.map((byte) => String.fromCharCode(byte))
.join('')

return btoa(ivStr + cipherStr)
}

export async function decrypt(c: Context, message: string) {
const key = (c.env('crypto_key') ?? c.env('CRYPTO_KEY')) as string

const iv = atob(message).slice(0, 12)
const alg = {
name: 'AES-GCM',
iv: new Uint8Array(
Array.from(iv).map((char) => char.charCodeAt(0)),
),
}
const cryptoKey = await crypto.subtle.importKey(
'raw',
decode(key).buffer,
alg,
true,
['encrypt', 'decrypt'],
)
const cipherStr = atob(message).slice(12)
const cipherBuf = new Uint8Array(
Array.from(cipherStr).map((char) => char.charCodeAt(0)),
)
const buf = await crypto.subtle.decrypt(alg, cryptoKey, cipherBuf)

return new TextDecoder().decode(buf)
}
31 changes: 6 additions & 25 deletions jwt.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
// Copyright 2023 Samuel Kopp. All rights reserved. Apache-2.0 license.
import {
decode,
encode,
} from 'https://deno.land/std@0.198.0/encoding/base64.ts'
import { decode } from 'https://deno.land/std@0.198.0/encoding/base64.ts'
import * as Jwt from 'https://deno.land/x/djwt@v2.8/mod.ts'
import { Context } from './mod.ts'

Expand All @@ -23,19 +20,7 @@ interface Payload {
[key: string]: unknown
}

export async function createKey() {
const key = await crypto.subtle.generateKey(
{ name: 'HMAC', hash: 'SHA-512' },
true,
['sign', 'verify'],
)

const exportedKey = await crypto.subtle.exportKey('raw', key)

return encode(exportedKey)
}

export function importKey(key: string) {
function importKey(key: string) {
return crypto.subtle.importKey(
'raw',
decode(key).buffer,
Expand All @@ -50,16 +35,14 @@ export function importKey(key: string) {
*/
// deno-lint-ignore ban-types
export async function sign<T extends Record<string, unknown> = {}>(
secret: string | CryptoKey | Context,
secret: string | Context,
payload: T & Payload,
) {
const key = typeof secret === 'string'
? await importKey(secret)
: secret instanceof Context
? await importKey(
: await importKey(
(secret.env('jwt_secret') ?? secret.env('JWT_SECRET')) as string,
)
: secret

const { exp, nbf, ...rest } = payload

Expand All @@ -74,18 +57,16 @@ export async function sign<T extends Record<string, unknown> = {}>(
* Verify the validity of a JWT.
*/
export async function verify<T extends Record<string, unknown> = Payload>(
secret: string | CryptoKey | Context,
secret: string | Context,
token: string,
options?: Jwt.VerifyOptions,
) {
try {
const key = typeof secret === 'string'
? await importKey(secret)
: secret instanceof Context
? await importKey(
: await importKey(
(secret.env('jwt_secret') ?? secret.env('JWT_SECRET')) as string,
)
: secret

return await Jwt.verify(token, key, options) as Jwt.Payload & T
} catch (_err) {
Expand Down
Loading

0 comments on commit 29f5fe8

Please sign in to comment.