mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
Merge branch 'main' into e2e
This commit is contained in:
commit
32e2479793
51
.design/DESIGN.md
Normal file
51
.design/DESIGN.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Databag Design Guidelines & Resources
|
||||
|
||||
A collection of design contribution guidelines and resources for our Databag product.
|
||||
|
||||
> **All participating designers are highly encouraged to shape and evolve these guidelines!**
|
||||
|
||||
## Welcome
|
||||
|
||||
Databag is a product targeted for the decentralized web community. It is a federeated messenger that gives people back their privacy and control of their data.
|
||||
|
||||
## How to contribute design
|
||||
|
||||
1. Check out open [issues](https://github.com/balzack/databag/issues) here on GitHub (we label them with `design: required`)
|
||||
2. Feel free to open an issue on your own if you find something you would like to contribute to the project and use the `design: idea` label for it.
|
||||
3. There are no existing figma files yet, for now create new ones and share them publicly
|
||||
4. Add your contributions to an issue and we promise we will review your contribution carefully and foster discussions
|
||||
|
||||
**We encourage you to:**
|
||||
|
||||
- Get in touch with the team by starting a discussion on [GitHub](https://github.com/balzack/databag/discussions).
|
||||
|
||||
## Target audience
|
||||
|
||||
The initial users are typically very technical, but hopefully this product will be embedded into consumer electronics and reach a much less technical audience. The design may benefit by exposing some technical attributes. For example, with a self-hosted product the users my be interested in knowing where their data actually lives.
|
||||
|
||||
## Design relevant materials
|
||||
|
||||
Here is a list of design relevant information and materials:
|
||||
|
||||
### Fonts
|
||||
|
||||
Currently we're using Roboto everywhere.
|
||||
|
||||
### Colors
|
||||
|
||||
We're using shades of green deliberately staying away from the corporate blue. The current background color is (#8fbea7) with the primary button color (#448866).
|
||||
|
||||
### Logos
|
||||
|
||||
We don't really have a logo yet, and are currently just using a scaled image from the app as the [icon](https://github.com/balzack/databag/blob/main/doc/icon.png)
|
||||
|
||||
### Design Files, Screenshots, etc
|
||||
|
||||
We don't have any design files yet. Initially you can reference screenshots for the [browser app](https://github.com/balzack/databag/blob/main/doc/browser.png) and [mobile app](https://github.com/balzack/databag/blob/main/doc/mobile.png)
|
||||
|
||||
## License
|
||||
|
||||
All design work is licensed under the
|
||||
[Apache-2.0](https://github.com/balzack/databag/blob/main/LICENSE)
|
||||
|
||||
[(Back to top)](#-table-of-contents)
|
20
README.md
20
README.md
@ -1,3 +1,6 @@
|
||||
|
||||
[![contribute.design](https://contribute.design/api/shield/balzack/databag)](https://contribute.design/balzack/databag)
|
||||
|
||||
<div align="center">
|
||||
<a href="#"><img src="/doc/icon.png" width="8%" style="border-radius:50%"></a>
|
||||
<h3 align="center">Databag</h3>
|
||||
@ -19,7 +22,7 @@
|
||||
Databag is a self-hosted messaging service. Notable features include:
|
||||
- Public-private key based identity (not bound to any blockchain or hosting domain)
|
||||
- Federated (accounts on different nodes can communicate)
|
||||
- Topic based threads (messages grouped by topic not contacts)
|
||||
- Topic based threads (messages organized by topic not contacts)
|
||||
- Lightweight (server runs on a raspberry pi zero v1.3)
|
||||
- Decentralized (direct communication between app and contact's node)
|
||||
- Low latency (use of websockets for push events to avoid polling)
|
||||
@ -30,10 +33,10 @@ Databag is a self-hosted messaging service. Notable features include:
|
||||
<br>
|
||||
<p align="center">
|
||||
<a href="https://apps.apple.com/us/app/databag/id6443741428">
|
||||
<img src="/doc/astore.png" width="15%">
|
||||
<img src="/doc/astore.png" width="18%">
|
||||
</a>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.databag">
|
||||
<img src="/doc/gplay.png" width="15%">
|
||||
<img src="/doc/gplay.png" width="18%">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@ -43,7 +46,10 @@ The app is available in the google and apple stores. You can also test out the p
|
||||
|
||||
To use databag, you will need a DNS name pointing to your node with a certificate. You can deploy a node manually, but you will have a much easier time using a container service. Containers for arm64 and amd64 are available [here](https://hub.docker.com/r/balzack/databag/tags).
|
||||
|
||||
Instruction for installing without a container on a Raspberry Pi Zero are [here](/doc/pizero.md).
|
||||
### Docker Compose Command
|
||||
|
||||
From the net/container sub directory:
|
||||
- sudo docker-compose -f compose.yaml -p databag up
|
||||
|
||||
### Example with Portainer and Nginx Proxy Manager
|
||||
|
||||
@ -71,3 +77,9 @@ From Your Browser:
|
||||
- Click 'Save'
|
||||
- Click the user icon to generate a new account link
|
||||
- Follow the link to create an account
|
||||
|
||||
### Other installation options
|
||||
|
||||
Instruction for installing without a container on a Raspberry Pi Zero are [here](/doc/pizero.md).
|
||||
|
||||
Instruction for installing without a container in AWS are [here](/doc/aws.md).
|
||||
|
@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.4</string>
|
||||
<string>1.5</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
@ -69,6 +69,9 @@ export function Admin() {
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.version}>
|
||||
<Text style={styles.versiontext}>v{ state.version }</Text>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
|
@ -108,6 +108,13 @@ export const styles = StyleSheet.create({
|
||||
nologintext: {
|
||||
color: Colors.disabled,
|
||||
},
|
||||
|
||||
version: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
versiontext: {
|
||||
color: Colors.grey,
|
||||
fontSize: 14,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -17,12 +17,17 @@ export function useAdmin() {
|
||||
server: null,
|
||||
token: null,
|
||||
plainText: false,
|
||||
version: null,
|
||||
});
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
updateState({ version: app.state.version });
|
||||
}, [app]);
|
||||
|
||||
const checkStatus = async () => {
|
||||
try {
|
||||
updateState({ unclaimed: status });
|
||||
|
@ -20,6 +20,8 @@ export function useAppContext() {
|
||||
loginTimestamp: null,
|
||||
disconnected: null,
|
||||
deviceToken: null,
|
||||
loggingOut: false,
|
||||
version: getVersion(),
|
||||
});
|
||||
const store = useContext(StoreContext);
|
||||
const account = useContext(AccountContext);
|
||||
@ -27,6 +29,7 @@ export function useAppContext() {
|
||||
const card = useContext(CardContext);
|
||||
const channel = useContext(ChannelContext);
|
||||
const count = useRef(0);
|
||||
const delay = useRef(0);
|
||||
|
||||
const ws = useRef(null);
|
||||
|
||||
@ -83,7 +86,7 @@ export function useAppContext() {
|
||||
username: getUsername,
|
||||
create: async (server, username, password, token) => {
|
||||
await addAccount(server, username, password, token);
|
||||
const access = await setLogin(username, server, password, getApplicatioName(), getVersion(), getDeviceId(), state.deviceToken, notifications)
|
||||
const access = await setLogin(username, server, password, getApplicationName(), getVersion(), getDeviceId(), state.deviceToken, notifications)
|
||||
await store.actions.setSession({ ...access, server});
|
||||
await setSession({ ...access, server });
|
||||
if (access.pushSupported) {
|
||||
@ -108,6 +111,7 @@ export function useAppContext() {
|
||||
}
|
||||
},
|
||||
logout: async () => {
|
||||
updateState({ loggingOut: true });
|
||||
try {
|
||||
await messaging().deleteToken();
|
||||
const token = await messaging().getToken();
|
||||
@ -119,6 +123,7 @@ export function useAppContext() {
|
||||
}
|
||||
await clearSession();
|
||||
await store.actions.clearSession();
|
||||
updateState({ loggingOut: false });
|
||||
},
|
||||
remove: async () => {
|
||||
await removeProfile(state.server, state.appToken);
|
||||
@ -131,6 +136,7 @@ export function useAppContext() {
|
||||
clearWebsocket();
|
||||
ws.current = new WebSocket(`wss://${server}/status`);
|
||||
ws.current.onmessage = (ev) => {
|
||||
delay.current = 0;
|
||||
try {
|
||||
const rev = JSON.parse(ev.data);
|
||||
try {
|
||||
@ -159,9 +165,10 @@ export function useAppContext() {
|
||||
ws.current.onclose = () => {}
|
||||
ws.current.onopen = () => {}
|
||||
ws.current.onerror = () => {}
|
||||
delay.current = 1;
|
||||
setWebsocket(server, token);
|
||||
}
|
||||
}, 1000)
|
||||
}, 1000 * delay.current)
|
||||
}
|
||||
ws.current.onopen = () => {
|
||||
ws.current.send(JSON.stringify({ AppToken: token }))
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Keyboard, KeyboardAvoidingView, ActivityIndicator, Modal, Platform, TextInput, View, TouchableOpacity, Text, } from 'react-native';
|
||||
import { useKeepAwake } from 'expo-keep-awake';
|
||||
import { FlatList, ScrollView } from '@stream-io/flat-list-mvcp';
|
||||
import { memo, useState, useRef, useEffect } from 'react';
|
||||
import { useConversation } from './useConversation.hook';
|
||||
@ -59,8 +58,6 @@ export function ConversationBody() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useKeepAwake();
|
||||
|
||||
const latch = () => {
|
||||
if (!state.momentum) {
|
||||
actions.latch();
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Image, View, Text, TouchableOpacity } from 'react-native';
|
||||
import { useRef } from 'react';
|
||||
import { useKeepAwake } from 'expo-keep-awake';
|
||||
import Colors from 'constants/Colors';
|
||||
import { Video, AVPlaybackStatus } from 'expo-av';
|
||||
import { useAudioAsset } from './useAudioAsset.hook';
|
||||
@ -12,6 +13,8 @@ export function AudioAsset({ topicId, asset, dismiss }) {
|
||||
|
||||
const { state, actions } = useAudioAsset(topicId, asset);
|
||||
|
||||
useKeepAwake();
|
||||
|
||||
const player = useRef(null);
|
||||
|
||||
return (
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ActivityIndicator, Image, View, TouchableOpacity } from 'react-native';
|
||||
import Colors from 'constants/Colors';
|
||||
import { useKeepAwake } from 'expo-keep-awake';
|
||||
import { Video, AVPlaybackStatus } from 'expo-av';
|
||||
import { useVideoAsset } from './useVideoAsset.hook';
|
||||
import { styles } from './VideoAsset.styled';
|
||||
@ -9,6 +10,8 @@ export function VideoAsset({ topicId, asset, dismiss }) {
|
||||
|
||||
const { state, actions } = useVideoAsset(topicId, asset);
|
||||
|
||||
useKeepAwake();
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity activeOpacity={1} style={styles.container} onPress={actions.showControls}>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useEffect, useContext } from 'react';
|
||||
import { KeyboardAvoidingView, Modal, Alert, TextInput, ScrollView, View, Switch, TouchableOpacity, Text } from 'react-native';
|
||||
import { ActivityIndicator, KeyboardAvoidingView, Modal, Alert, TextInput, ScrollView, View, Switch, TouchableOpacity, Text } from 'react-native';
|
||||
import { styles } from './Profile.styled';
|
||||
import { useProfile } from './useProfile.hook';
|
||||
import Ionicons from '@expo/vector-icons/AntDesign';
|
||||
@ -21,9 +21,16 @@ export function Profile({ navigation }) {
|
||||
</View>
|
||||
),
|
||||
headerRight: () => (
|
||||
<TouchableOpacity style={styles.action} onPress={logout} onLongPress={actions.showDelete}>
|
||||
<Ionicons name="logout" size={22} color={Colors.primary} />
|
||||
</TouchableOpacity>
|
||||
<>
|
||||
{ state.loggingOut && (
|
||||
<ActivityIndicator style={styles.action} size="small" />
|
||||
)}
|
||||
{ !state.loggingOut && (
|
||||
<TouchableOpacity style={styles.action} onPress={logout} onLongPress={actions.showDelete}>
|
||||
<Ionicons name="logout" size={22} color={Colors.primary} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
});
|
||||
}
|
||||
@ -71,9 +78,14 @@ export function Profile({ navigation }) {
|
||||
<SafeAreaView style={styles.drawer} edges={['top', 'bottom', 'right']}>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.headerText} numberOfLines={1}>{ `${state.handle}@${state.node}` }</Text>
|
||||
<TouchableOpacity onPress={logout} onLongPress={actions.showDelete}>
|
||||
<Ionicons name="logout" size={16} color={Colors.grey} />
|
||||
</TouchableOpacity>
|
||||
{ state.loggingOut && (
|
||||
<ActivityIndicator size="small" />
|
||||
)}
|
||||
{ !state.loggingOut && (
|
||||
<TouchableOpacity onPress={logout} onLongPress={actions.showDelete}>
|
||||
<Ionicons name="logout" size={16} color={Colors.grey} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
<ProfileBody />
|
||||
<TouchableOpacity style={styles.erase} activeOpacity={1} onPress={actions.showDelete}>
|
||||
|
@ -44,6 +44,7 @@ export const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-end',
|
||||
justifyContent: 'center',
|
||||
height: 32,
|
||||
},
|
||||
headerText: {
|
||||
paddingLeft: 16,
|
||||
|
@ -14,6 +14,7 @@ export function useProfile() {
|
||||
showDelete: false,
|
||||
tabbed: null,
|
||||
confirmDelete: null,
|
||||
logginOut: false,
|
||||
});
|
||||
|
||||
const app = useContext(AppContext);
|
||||
@ -42,8 +43,8 @@ export function useProfile() {
|
||||
}, [profile]);
|
||||
|
||||
useEffect(() => {
|
||||
const { disconnected } = app.state;
|
||||
updateState({ disconnected });
|
||||
const { disconnected, loggingOut } = app.state;
|
||||
updateState({ disconnected, loggingOut });
|
||||
}, [app]);
|
||||
|
||||
const actions = {
|
||||
|
1
doc/CHANGELOG.md
Normal file
1
doc/CHANGELOG.md
Normal file
@ -0,0 +1 @@
|
||||
|
97
doc/aws.md
Normal file
97
doc/aws.md
Normal file
@ -0,0 +1,97 @@
|
||||
# Install Databag in AWS
|
||||
|
||||
These instructions assume you have the following setup:
|
||||
- an AMD64 Ubuntu EC2 instance with incoming ports 443 and 80<br/>
|
||||
- an EFS instance<br/>
|
||||
- a domain name pointing the the IP of your EC2 instance<br/>
|
||||
|
||||
## Step 1: obtain cert
|
||||
sudo apt-get install certbot<br/>
|
||||
sudo certbot certonly --standalone -d [dns name]<br/>
|
||||
|
||||
## Step 2: install databag dependencies
|
||||
sudo apt-get -y install ffmpeg<br/>
|
||||
sudo apt-get -y install curl<br/>
|
||||
sudo apt-get -y install net-tools<br/>
|
||||
sudo apt-get -y install jq<br/>
|
||||
sudo apt-get -y install netcat<br/>
|
||||
sudo apt-get -y install unzip<br/>
|
||||
sudo apt-get -y install wget<br/>
|
||||
sudo apt-get -y install git<br/>
|
||||
sudo apt-get -y install vim<br/>
|
||||
sudo apt-get -y install fail2ban<br/>
|
||||
sudo apt-get -y install imagemagick-6.q16<br/>
|
||||
sudo apt-get -y install build-essential<br/>
|
||||
sudo apt-get -y install sqlite3<br/>
|
||||
sudo apt-get -y install openssh-client<br/>
|
||||
apt-get -y install npm<br/>
|
||||
apt-get -y upgrade<br/>
|
||||
npm install --global yarn<br/>
|
||||
npm install -g n<br/>
|
||||
n stable<br/>
|
||||
|
||||
## Step 3: download and install golang
|
||||
wget https://go.dev/dl/go1.19.3.linux-amd64.tar.gz<br/>
|
||||
sudo tar -C /usr/local -xzf go1.19.3.linux-amd64.tar.gz<br/>
|
||||
|
||||
## Step 4: clone and build the server
|
||||
mkdir /app<br/>
|
||||
cd /app<br/>
|
||||
git clone https://github.com/balzack/databag.git<br/>
|
||||
cd /app/databag/net/web<br/>
|
||||
yarn config set network-timeout 300000<br/>
|
||||
yarn --cwd /app/databag/net/web install<br/>
|
||||
yarn --cwd /app/databag/net/web build<br/>
|
||||
cd /app/databag/net/server<br/>
|
||||
/usr/local/go/bin/go build databag<br/>
|
||||
|
||||
## Step 5: setup databag paths
|
||||
mkdir -p /var/lib/databag/assets<br/>
|
||||
mkdir -p /opt/databag/transform<br/>
|
||||
cp /app/databag/net/container/transform/* /opt/databag/transform/<br/>
|
||||
|
||||
## Step 6: mount EFS to store assets
|
||||
sudo apt-get update<br/>
|
||||
sudo apt-get -y install git binutils<br/>
|
||||
git clone https://github.com/aws/efs-utils<br/>
|
||||
cd efs-utils<br/>
|
||||
./build-deb.sh<br/>
|
||||
sudo apt-get -y install ./build/amazon-efs-utils*deb<br/>
|
||||
sudo mount -t efs file-system-id /var/lib/databag/assets<br/>
|
||||
|
||||
## Step 7: initialize the internal datbase
|
||||
sqlite3 /var/lib/databag/databag.db "VACUUM;"<br/>
|
||||
sqlite3 /var/lib/databag/databag.db "CREATE TABLE IF NOT EXISTS 'configs' ('id' integer NOT NULL UNIQUE,'config_id' text NOT NULL,'str_value' text,'num_value' integer,'bool_value' numeric,'bin_value' blob,PRIMARY KEY ('id'));"<br/>
|
||||
sqlite3 /var/lib/databag/databag.db "CREATE UNIQUE INDEX IF NOT EXISTS 'idx_configs_config_id' ON 'configs'('config_id');"<br/>
|
||||
sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, str_value) values ('asset_path', '/var/lib/databag/assets');"<br/>
|
||||
sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, str_value) values ('script_path', '/opt/databag/transform/');"<br/>
|
||||
|
||||
## Step 8: launch the server
|
||||
cd /app/databag/net/server<br/>
|
||||
nohup nice -n -5 /usr/local/go/bin/go run databag [dns name] &<br/>
|
||||
|
||||
## Step 9: configure the server
|
||||
Open your brower to https://[dns name]<br/>
|
||||
Click the 'cog' in the upper right<br/>
|
||||
Set an admin password<br/>
|
||||
Select the 'cog' to bring up the settings modal<br/>
|
||||
- set your hostname as [dns name]<br/>
|
||||
- set the key to RSA 2048<br/>
|
||||
- enable push notifications<br/>
|
||||
- enable images<br/>
|
||||
- disable audio<br/>
|
||||
- disable video<br/>
|
||||
|
||||
## Step 10: create accounts
|
||||
Still in the admin dashboard<br/>
|
||||
Click the 'add-user' button<br/>
|
||||
Open the link in a new tab<br/>
|
||||
Set a username and password<br/>
|
||||
Setup your profile<br/>
|
||||
Connect with contacts on other federated instances<br/>
|
||||
|
||||
## Step 11: host for your friends and family
|
||||
Back in the admin dashboard<br/>
|
||||
Click the 'add-user' and send the link to anyone you want to host<br/>
|
||||
|
||||
|
10
net/container/compose.yaml
Normal file
10
net/container/compose.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
version: "3.9"
|
||||
services:
|
||||
databag:
|
||||
container_name: databag
|
||||
image: balzack/databag:latest
|
||||
ports:
|
||||
- "7000:7000"
|
||||
volumes:
|
||||
- ./databag-data:/data
|
||||
|
@ -21,11 +21,11 @@ func main() {
|
||||
args := os.Args
|
||||
if len(args) == 3 {
|
||||
port := ":" + args[2]
|
||||
path := "etc/letsencrypt/live/" + args[1]
|
||||
path := "/etc/letsencrypt/live/" + args[1]
|
||||
log.Printf("starting server at: " + path + " " + port);
|
||||
log.Fatal(http.ListenAndServeTLS(port, path + "/fullchain.pem", path + "/privkey.pem", handlers.CORS(origins, methods)(router)))
|
||||
} else if len(args) == 2 {
|
||||
path := "etc/letsencrypt/live/" + args[1]
|
||||
path := "/etc/letsencrypt/live/" + args[1]
|
||||
log.Printf("starting server at: " + path);
|
||||
log.Fatal(http.ListenAndServeTLS(":443", path + "/fullchain.pem", path + "/privkey.pem", handlers.CORS(origins, methods)(router)))
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user