fix some issues

This commit is contained in:
EthanShoeDev
2025-09-10 06:20:39 -04:00
parent 7ce3e20e73
commit cdefdd4664
3 changed files with 93 additions and 36 deletions

View File

@@ -348,23 +348,29 @@ const styles = StyleSheet.create({
},
card: {
backgroundColor: '#111B34',
borderRadius: 16,
padding: 20,
borderRadius: 20,
padding: 24,
marginHorizontal: 4,
shadowColor: '#000',
shadowOpacity: 0.2,
shadowRadius: 12,
elevation: 6,
shadowOpacity: 0.3,
shadowRadius: 16,
shadowOffset: { width: 0, height: 4 },
elevation: 8,
borderWidth: 1,
borderColor: '#1E293B',
},
title: {
fontSize: 22,
fontSize: 24,
fontWeight: '700',
color: '#E5E7EB',
marginBottom: 4,
marginBottom: 6,
letterSpacing: 0.5,
},
subtitle: {
fontSize: 14,
fontSize: 15,
color: '#9AA0A6',
marginBottom: 16,
marginBottom: 24,
lineHeight: 20,
},
inputGroup: {
marginBottom: 12,
@@ -391,7 +397,7 @@ const styles = StyleSheet.create({
fontSize: 12,
},
actions: {
marginTop: 8,
marginTop: 20,
},
mutedText: {
color: '#9AA0A6',
@@ -399,14 +405,20 @@ const styles = StyleSheet.create({
},
submitButton: {
backgroundColor: '#2563EB',
borderRadius: 10,
paddingVertical: 14,
borderRadius: 12,
paddingVertical: 16,
alignItems: 'center',
shadowColor: '#2563EB',
shadowOpacity: 0.3,
shadowRadius: 8,
shadowOffset: { width: 0, height: 2 },
elevation: 4,
},
submitButtonText: {
color: '#FFFFFF',
fontWeight: '700',
fontSize: 16,
letterSpacing: 0.5,
},
buttonDisabled: {
backgroundColor: '#3B82F6',
@@ -416,15 +428,16 @@ const styles = StyleSheet.create({
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '#2A3655',
borderRadius: 10,
paddingVertical: 12,
borderRadius: 12,
paddingVertical: 14,
alignItems: 'center',
marginTop: 8,
marginTop: 12,
},
secondaryButtonText: {
color: '#C6CBD3',
fontWeight: '600',
fontSize: 14,
letterSpacing: 0.3,
},
listSection: {
marginTop: 20,

View File

@@ -108,13 +108,16 @@ export function PickerField<T>(
return (
<View style={styles.inputGroup}>
{label ? <Text style={styles.label}>{label}</Text> : null}
<View style={[styles.input, styles.pickerContainer]}>
<Picker<T>
style={styles.picker}
selectedValue={field.state.value}
onValueChange={(itemValue) => field.handleChange(itemValue)}
{...rest}
>
{props.children}
</Picker>
</View>
<FieldInfo />
</View>
);
@@ -171,7 +174,7 @@ export const { useAppForm, withForm, withFieldGroup } = createFormHook({
const styles = StyleSheet.create({
inputGroup: {
marginBottom: 12,
marginBottom: 16,
},
label: {
marginBottom: 6,
@@ -214,4 +217,11 @@ const styles = StyleSheet.create({
color: '#FCA5A5',
fontSize: 12,
},
pickerContainer: {
paddingHorizontal: 8,
paddingVertical: 4,
},
picker: {
color: '#E5E7EB',
},
});

View File

@@ -7,6 +7,7 @@ import { queryClient } from './utils';
const keys = {
storagePrefix: 'privateKey_',
manifestKey: 'privateKeysManifest',
chunkSize: 1800, // Safely under 2048 byte limit
} as const;
const keyManifestSchema = z.object({
@@ -15,7 +16,8 @@ const keyManifestSchema = z.object({
z.object({
id: z.string(),
priority: z.number(),
createdAt: z.date(),
createdAtMs: z.int(),
chunkCount: z.number().default(1),
}),
),
});
@@ -31,6 +33,19 @@ async function getKeyManifest() {
return keyManifestSchema.parse(manifest);
}
// Utility functions for chunking large data
function splitIntoChunks(data: string, chunkSize: number): string[] {
const chunks: string[] = [];
for (let i = 0; i < data.length; i += chunkSize) {
chunks.push(data.substring(i, i + chunkSize));
}
return chunks;
}
function getChunkKey(keyId: string, chunkIndex: number): string {
return `${keys.storagePrefix}${keyId}_chunk_${chunkIndex}`;
}
async function savePrivateKey(params: {
keyId: string;
privateKey: string;
@@ -42,17 +57,24 @@ async function savePrivateKey(params: {
if (existingKey) throw new Error('Key already exists');
// Split the private key into chunks if it's too large
const chunks = splitIntoChunks(params.privateKey, keys.chunkSize);
const chunkCount = chunks.length;
const newKey = {
id: params.keyId,
priority: params.priority,
createdAt: new Date(),
createdAtMs: Date.now(),
chunkCount,
};
manifest.keys.push(newKey);
await SecureStore.setItemAsync(
`${keys.storagePrefix}${params.keyId}`,
params.privateKey,
);
// Save each chunk separately
for (let i = 0; i < chunks.length; i++) {
await SecureStore.setItemAsync(getChunkKey(params.keyId, i), chunks[i]!);
}
await SecureStore.setItemAsync(keys.manifestKey, JSON.stringify(manifest));
await queryClient.invalidateQueries({ queryKey: [keyQueryKey] });
}
@@ -61,10 +83,16 @@ async function getPrivateKey(keyId: string) {
const manifest = await getKeyManifest();
const key = manifest.keys.find((key) => key.id === keyId);
if (!key) throw new Error('Key not found');
const privateKey = await SecureStore.getItemAsync(
`${keys.storagePrefix}${keyId}`,
);
if (!privateKey) throw new Error('Key not found');
// Reassemble the private key from chunks
const chunks: string[] = [];
for (let i = 0; i < key.chunkCount; i++) {
const chunk = await SecureStore.getItemAsync(getChunkKey(keyId, i));
if (!chunk) throw new Error(`Key chunk ${i} not found`);
chunks.push(chunk);
}
const privateKey = chunks.join('');
return {
...key,
privateKey,
@@ -75,9 +103,15 @@ async function deletePrivateKey(keyId: string) {
const manifest = await getKeyManifest();
const key = manifest.keys.find((key) => key.id === keyId);
if (!key) throw new Error('Key not found');
manifest.keys = manifest.keys.filter((key) => key.id !== keyId);
// Delete all chunks for this key
for (let i = 0; i < key.chunkCount; i++) {
await SecureStore.deleteItemAsync(getChunkKey(keyId, i));
}
await SecureStore.setItemAsync(keys.manifestKey, JSON.stringify(manifest));
await SecureStore.deleteItemAsync(`${keys.storagePrefix}${keyId}`);
await queryClient.invalidateQueries({ queryKey: [keyQueryKey] });
}
@@ -235,9 +269,9 @@ async function generateKeyPair(params: {
}) {
const keyPair = await SSHClient.generateKeyPair(
params.type,
params.passphrase,
params.passphrase ?? '',
params.keySize,
params.comment,
params.comment ?? '',
);
return keyPair;
}