Compare commits
3 Commits
fc33a9c051
...
0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4b627b6bb | ||
|
|
921c4a29a7 | ||
|
|
5e633d9dd6 |
140
bin/uploadArtifacts.js
Normal file
140
bin/uploadArtifacts.js
Normal file
@@ -0,0 +1,140 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { execSync } from 'child_process'
|
||||
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
dotenv.config({ quiet: true })
|
||||
|
||||
const DIST_DIR = path.resolve('dist')
|
||||
const BUCKET = 'dist'
|
||||
|
||||
const client = new S3Client({
|
||||
region: 'us-east-1',
|
||||
endpoint: 'https://s3.takerofnotes.com',
|
||||
forcePathStyle: true,
|
||||
credentials: {
|
||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
||||
},
|
||||
})
|
||||
|
||||
function getContentType(file) {
|
||||
if (file.endsWith('.yml')) return 'text/yaml'
|
||||
if (file.endsWith('.json')) return 'application/json'
|
||||
if (file.endsWith('.AppImage')) return 'application/octet-stream'
|
||||
if (file.endsWith('.deb')) return 'application/vnd.debian.binary-package'
|
||||
if (file.endsWith('.exe'))
|
||||
return 'application/vnd.microsoft.portable-executable'
|
||||
if (file.endsWith('.dmg')) return 'application/x-apple-diskimage'
|
||||
return 'application/octet-stream'
|
||||
}
|
||||
|
||||
function getCurrentTag() {
|
||||
try {
|
||||
const tag = execSync('git describe --tags --exact-match 2>/dev/null', {
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
}).trim()
|
||||
return tag
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function isGitTagDirty() {
|
||||
try {
|
||||
const status = execSync('git status --porcelain', {
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
}).trim()
|
||||
return status.length > 0
|
||||
} catch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadFile(filePath, platform, version) {
|
||||
const fileStream = fs.createReadStream(filePath)
|
||||
const fileName = path.basename(filePath)
|
||||
const key = `${platform}/${version}/${fileName}`
|
||||
|
||||
console.log(`Uploading ${fileName} → ${key}`)
|
||||
|
||||
await client.send(
|
||||
new PutObjectCommand({
|
||||
Bucket: BUCKET,
|
||||
Key: key,
|
||||
Body: fileStream,
|
||||
ContentType: getContentType(fileName),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const platform = process.argv[2]
|
||||
const version = process.argv[3]
|
||||
|
||||
if (!platform || !version) {
|
||||
console.error('Usage: node uploadArtifacts.js <platform> <version>')
|
||||
console.error('Example: node uploadArtifacts.js win 1.0.0')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const currentTag = getCurrentTag()
|
||||
if (!currentTag) {
|
||||
console.log(
|
||||
'No git tag found. Skipping upload (artifacts are only uploaded on tagged commits).',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (currentTag !== version && !currentTag.startsWith(version)) {
|
||||
console.log(
|
||||
`Git tag (${currentTag}) does not match version (${version}). Skipping upload.`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// if (isGitTagDirty()) {
|
||||
// console.log('Git working directory is dirty. Skipping upload.')
|
||||
// return
|
||||
// }
|
||||
|
||||
console.log(
|
||||
`Uploading artifacts for ${platform} v${version} (tag: ${currentTag})`,
|
||||
)
|
||||
|
||||
if (!fs.existsSync(DIST_DIR)) {
|
||||
throw new Error('dist directory not found. Run build first.')
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(DIST_DIR)
|
||||
|
||||
const uploadTargets = files.filter((file) => {
|
||||
return (
|
||||
file.endsWith('.yml') ||
|
||||
file.endsWith('.AppImage') ||
|
||||
file.endsWith('.deb') ||
|
||||
file.endsWith('.exe') ||
|
||||
file.endsWith('.dmg')
|
||||
)
|
||||
})
|
||||
|
||||
if (uploadTargets.length === 0) {
|
||||
console.log('No artifacts found to upload.')
|
||||
return
|
||||
}
|
||||
|
||||
for (const file of uploadTargets) {
|
||||
const fullPath = path.join(DIST_DIR, file)
|
||||
await uploadFile(fullPath, platform, version)
|
||||
}
|
||||
|
||||
console.log('Upload complete.')
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -30,14 +30,11 @@ dmg:
|
||||
linux:
|
||||
target:
|
||||
- AppImage
|
||||
- snap
|
||||
# - snap
|
||||
- deb
|
||||
maintainer: electronjs.org
|
||||
category: Utility
|
||||
appImage:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
npmRebuild: false
|
||||
publish:
|
||||
provider: s3
|
||||
bucket: dist
|
||||
endpoint: https://s3.takerofnotes.com
|
||||
publish: null
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "dotenv/config";
|
||||
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
|
||||
import { app, ipcMain, BrowserWindow, shell } from "electron";
|
||||
import { app, ipcMain, BrowserWindow, dialog, shell } from "electron";
|
||||
import filesystemPlugin from "@takerofnotes/plugin-filesystem";
|
||||
import supabasePlugin from "@takerofnotes/plugin-supabase";
|
||||
import s3Plugin from "@takerofnotes/plugin-s3";
|
||||
@@ -577,6 +577,12 @@ app.whenReady().then(async () => {
|
||||
);
|
||||
}
|
||||
});
|
||||
ipcMain.handle("open-directory-dialog", async () => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
properties: ["openDirectory"]
|
||||
});
|
||||
return result.canceled ? null : result.filePaths[0];
|
||||
});
|
||||
electronApp.setAppUserModelId("com.electron");
|
||||
app.on("browser-window-created", (_, window) => {
|
||||
optimizer.watchWindowShortcuts(window);
|
||||
|
||||
@@ -38,6 +38,9 @@ const api = {
|
||||
},
|
||||
moveClosed: () => {
|
||||
ipcRenderer.invoke("move-closed");
|
||||
},
|
||||
openDirectoryDialog: () => {
|
||||
return ipcRenderer.invoke("open-directory-dialog");
|
||||
}
|
||||
};
|
||||
if (process.contextIsolated) {
|
||||
|
||||
635
package-lock.json
generated
635
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,10 +19,12 @@
|
||||
"build": "electron-vite build",
|
||||
"build:web": "vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"build:unpack": "npm run build && electron-builder --dir",
|
||||
"build:win": "npm run build && electron-builder --win",
|
||||
"build:mac": "npm run build && electron-builder --mac",
|
||||
"build:linux": "npm run build && electron-builder --linux"
|
||||
"build:linux": "npm run build && electron-builder --linux",
|
||||
"release:win": "node bin/uploadArtifacts.js win $npm_package_version",
|
||||
"release:mac": "node bin/uploadArtifacts.js mac $npm_package_version",
|
||||
"release:linux": "node bin/uploadArtifacts.js linux $npm_package_version"
|
||||
},
|
||||
"dependencies": {
|
||||
"@capacitor/android": "^8.3.0",
|
||||
@@ -64,6 +66,7 @@
|
||||
"vue-router": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.1025.0",
|
||||
"@capacitor/cli": "^8.3.0",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"electron": "^39.2.6",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dotenv/config'
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||
import { app, shell, BrowserWindow, ipcMain } from 'electron'
|
||||
import { app, shell, BrowserWindow, ipcMain, dialog } from 'electron'
|
||||
import filesystemPlugin from '@takerofnotes/plugin-filesystem'
|
||||
import supabasePlugin from '@takerofnotes/plugin-supabase'
|
||||
import s3Plugin from '@takerofnotes/plugin-s3'
|
||||
@@ -137,6 +137,13 @@ app.whenReady().then(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('open-directory-dialog', async () => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
properties: ['openDirectory'],
|
||||
})
|
||||
return result.canceled ? null : result.filePaths[0]
|
||||
})
|
||||
|
||||
electronApp.setAppUserModelId('com.electron')
|
||||
|
||||
app.on('browser-window-created', (_, window) => {
|
||||
|
||||
@@ -44,6 +44,9 @@ const api = {
|
||||
moveClosed: () => {
|
||||
ipcRenderer.invoke('move-closed')
|
||||
},
|
||||
openDirectoryDialog: () => {
|
||||
return ipcRenderer.invoke('open-directory-dialog')
|
||||
},
|
||||
}
|
||||
|
||||
if (process.contextIsolated) {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<transition name="menu">
|
||||
<div v-if="menuOpen" class="menu" ref="container">
|
||||
<Nav />
|
||||
<div
|
||||
v-if="menuOpen || lockOpen"
|
||||
:class="['menu', { locked: lockOpen }]"
|
||||
ref="container"
|
||||
>
|
||||
<Nav v-if="!lockOpen" />
|
||||
|
||||
<div class="menu-wrap layout-block">
|
||||
<new-note class="menu-item" @noteOpened="closeMenu" />
|
||||
@@ -32,6 +36,10 @@ import useMenu from '@/composables/useMenu'
|
||||
import { useRoute } from 'vue-router'
|
||||
import useExport from '@/composables/useExport'
|
||||
|
||||
const props = defineProps({
|
||||
lockOpen: { type: Boolean, default: () => false },
|
||||
})
|
||||
|
||||
const container = ref()
|
||||
|
||||
const { menuOpen, closeMenu } = useMenu()
|
||||
@@ -85,6 +93,10 @@ const handleExport = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
&.locked .menu-wrap {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&.menu-enter-active,
|
||||
&.menu-leave-active {
|
||||
transition: transform 300ms var(--ease-out-expo);
|
||||
|
||||
@@ -1,40 +1,58 @@
|
||||
<template>
|
||||
<div class="preferences-directory-input">
|
||||
<input v-model="model" type="text" />
|
||||
|
||||
<button @click="openDirectoryPicker">Browse</button>
|
||||
<label :for="key"> {{ label }}{{ required ? '*' : '' }} </label>
|
||||
|
||||
<div class="input-row">
|
||||
<input
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
webkitdirectory
|
||||
style="display: none"
|
||||
@change="handleDirectoryChange"
|
||||
v-model="model"
|
||||
:id="key"
|
||||
type="text"
|
||||
:placeholder="default"
|
||||
:required="required"
|
||||
readonly
|
||||
/>
|
||||
|
||||
<button @click="openDirectoryPicker" type="button">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const props = defineProps({
|
||||
label: String,
|
||||
key: String,
|
||||
required: Boolean,
|
||||
default: String,
|
||||
})
|
||||
|
||||
const model = defineModel()
|
||||
const fileInput = ref(null)
|
||||
|
||||
function openDirectoryPicker() {
|
||||
fileInput.value?.click()
|
||||
}
|
||||
|
||||
function handleDirectoryChange(event) {
|
||||
const files = event.target.files
|
||||
if (files.length > 0) {
|
||||
const path = files[0].webkitRelativePath || files[0].path
|
||||
const directoryPath = path.split('/').slice(0, -1).join('/')
|
||||
model.value = directoryPath || files[0].path
|
||||
const openDirectoryPicker = async () => {
|
||||
const path = await window.api.openDirectoryDialog()
|
||||
if (path) {
|
||||
model.value = path
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.preferences-directory-input {
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.input-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
input {
|
||||
flex: 1;
|
||||
border: 1px solid var(--grey-100);
|
||||
border-radius: 0.2em;
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
button {
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<preferences-text-input
|
||||
v-model.trim="config.encryptionKey"
|
||||
type="text"
|
||||
type="password"
|
||||
label="Encryption Key"
|
||||
key="encryptionKey"
|
||||
required
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<main v-if="loaded" class="directory layout-block">
|
||||
<main
|
||||
v-if="loaded && (categories?.length || notes?.length)"
|
||||
class="directory layout-block"
|
||||
>
|
||||
<category-row
|
||||
v-for="(category, i) in categories"
|
||||
:index="i"
|
||||
@@ -18,6 +21,10 @@
|
||||
<decryption-warning :failedIds="decryptionFailures" />
|
||||
</main>
|
||||
|
||||
<main v-else-if="loaded">
|
||||
<Menu lockOpen />
|
||||
</main>
|
||||
|
||||
<page-loading v-else />
|
||||
</template>
|
||||
|
||||
@@ -30,6 +37,7 @@ import NoteRow from '@/components/NoteRow.vue'
|
||||
import useNotes from '@/composables/useNotes'
|
||||
import NewNote from '@/components/NewNote.vue'
|
||||
import { useMagicKeys } from '@vueuse/core'
|
||||
import Menu from '@/components/Menu.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
Reference in New Issue
Block a user