diff --git a/.design/DESIGN.md b/.design/DESIGN.md
new file mode 100644
index 00000000..50e3cb2d
--- /dev/null
+++ b/.design/DESIGN.md
@@ -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)
diff --git a/README.md b/README.md
index 67b91e35..511b5482 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
+
+[![contribute.design](https://contribute.design/api/shield/balzack/databag)](https://contribute.design/balzack/databag)
+
Databag
@@ -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:
-
+
-
+
@@ -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).
diff --git a/app/mobile/ios/Databag/Info.plist b/app/mobile/ios/Databag/Info.plist
index ded82efc..879e8c36 100644
--- a/app/mobile/ios/Databag/Info.plist
+++ b/app/mobile/ios/Databag/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
-
1.4
+
1.5
CFBundleSignature
????
CFBundleVersion
diff --git a/app/mobile/src/access/admin/Admin.jsx b/app/mobile/src/access/admin/Admin.jsx
index 65228e67..a3694532 100644
--- a/app/mobile/src/access/admin/Admin.jsx
+++ b/app/mobile/src/access/admin/Admin.jsx
@@ -69,6 +69,9 @@ export function Admin() {
)}
+
+ v{ state.version }
+
);
diff --git a/app/mobile/src/access/admin/Admin.styled.js b/app/mobile/src/access/admin/Admin.styled.js
index 84402fb3..313c7a65 100644
--- a/app/mobile/src/access/admin/Admin.styled.js
+++ b/app/mobile/src/access/admin/Admin.styled.js
@@ -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,
+ },
})
diff --git a/app/mobile/src/access/admin/useAdmin.hook.js b/app/mobile/src/access/admin/useAdmin.hook.js
index 27530e40..5a9644b1 100644
--- a/app/mobile/src/access/admin/useAdmin.hook.js
+++ b/app/mobile/src/access/admin/useAdmin.hook.js
@@ -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 });
diff --git a/app/mobile/src/context/useAppContext.hook.js b/app/mobile/src/context/useAppContext.hook.js
index eaeb2804..1b2dd8b8 100644
--- a/app/mobile/src/context/useAppContext.hook.js
+++ b/app/mobile/src/context/useAppContext.hook.js
@@ -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 }))
diff --git a/app/mobile/src/session/conversation/Conversation.jsx b/app/mobile/src/session/conversation/Conversation.jsx
index 73195c5d..02bedf20 100644
--- a/app/mobile/src/session/conversation/Conversation.jsx
+++ b/app/mobile/src/session/conversation/Conversation.jsx
@@ -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();
diff --git a/app/mobile/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx b/app/mobile/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx
index 3a45fb58..e4185347 100644
--- a/app/mobile/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx
+++ b/app/mobile/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx
@@ -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 (
diff --git a/app/mobile/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx b/app/mobile/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx
index b5df8f7b..2605c1e8 100644
--- a/app/mobile/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx
+++ b/app/mobile/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx
@@ -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 (
diff --git a/app/mobile/src/session/profile/Profile.jsx b/app/mobile/src/session/profile/Profile.jsx
index 7ee75efa..42e77c05 100644
--- a/app/mobile/src/session/profile/Profile.jsx
+++ b/app/mobile/src/session/profile/Profile.jsx
@@ -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 }) {
),
headerRight: () => (
-
-
-
+ <>
+ { state.loggingOut && (
+
+ )}
+ { !state.loggingOut && (
+
+
+
+ )}
+ >
),
});
}
@@ -71,9 +78,14 @@ export function Profile({ navigation }) {
{ `${state.handle}@${state.node}` }
-
-
-
+ { state.loggingOut && (
+
+ )}
+ { !state.loggingOut && (
+
+
+
+ )}
diff --git a/app/mobile/src/session/profile/Profile.styled.js b/app/mobile/src/session/profile/Profile.styled.js
index f08b2d2a..32d4125b 100644
--- a/app/mobile/src/session/profile/Profile.styled.js
+++ b/app/mobile/src/session/profile/Profile.styled.js
@@ -44,6 +44,7 @@ export const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'flex-end',
justifyContent: 'center',
+ height: 32,
},
headerText: {
paddingLeft: 16,
diff --git a/app/mobile/src/session/profile/useProfile.hook.js b/app/mobile/src/session/profile/useProfile.hook.js
index 583ddefc..041e40a5 100644
--- a/app/mobile/src/session/profile/useProfile.hook.js
+++ b/app/mobile/src/session/profile/useProfile.hook.js
@@ -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 = {
diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/doc/CHANGELOG.md
@@ -0,0 +1 @@
+
diff --git a/doc/aws.md b/doc/aws.md
new file mode 100644
index 00000000..534b74b9
--- /dev/null
+++ b/doc/aws.md
@@ -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
+ - an EFS instance
+ - a domain name pointing the the IP of your EC2 instance
+
+## Step 1: obtain cert
+ sudo apt-get install certbot
+ sudo certbot certonly --standalone -d [dns name]
+
+## Step 2: install databag dependencies
+ sudo apt-get -y install ffmpeg
+ sudo apt-get -y install curl
+ sudo apt-get -y install net-tools
+ sudo apt-get -y install jq
+ sudo apt-get -y install netcat
+ sudo apt-get -y install unzip
+ sudo apt-get -y install wget
+ sudo apt-get -y install git
+ sudo apt-get -y install vim
+ sudo apt-get -y install fail2ban
+ sudo apt-get -y install imagemagick-6.q16
+ sudo apt-get -y install build-essential
+ sudo apt-get -y install sqlite3
+ sudo apt-get -y install openssh-client
+ apt-get -y install npm
+ apt-get -y upgrade
+ npm install --global yarn
+ npm install -g n
+ n stable
+
+## Step 3: download and install golang
+ wget https://go.dev/dl/go1.19.3.linux-amd64.tar.gz
+ sudo tar -C /usr/local -xzf go1.19.3.linux-amd64.tar.gz
+
+## Step 4: clone and build the server
+ mkdir /app
+ cd /app
+ git clone https://github.com/balzack/databag.git
+ cd /app/databag/net/web
+ yarn config set network-timeout 300000
+ yarn --cwd /app/databag/net/web install
+ yarn --cwd /app/databag/net/web build
+ cd /app/databag/net/server
+ /usr/local/go/bin/go build databag
+
+## Step 5: setup databag paths
+ mkdir -p /var/lib/databag/assets
+ mkdir -p /opt/databag/transform
+ cp /app/databag/net/container/transform/* /opt/databag/transform/
+
+## Step 6: mount EFS to store assets
+ sudo apt-get update
+ sudo apt-get -y install git binutils
+ git clone https://github.com/aws/efs-utils
+ cd efs-utils
+ ./build-deb.sh
+ sudo apt-get -y install ./build/amazon-efs-utils*deb
+ sudo mount -t efs file-system-id /var/lib/databag/assets
+
+## Step 7: initialize the internal datbase
+ sqlite3 /var/lib/databag/databag.db "VACUUM;"
+ 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'));"
+ sqlite3 /var/lib/databag/databag.db "CREATE UNIQUE INDEX IF NOT EXISTS 'idx_configs_config_id' ON 'configs'('config_id');"
+ sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, str_value) values ('asset_path', '/var/lib/databag/assets');"
+ sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, str_value) values ('script_path', '/opt/databag/transform/');"
+
+## Step 8: launch the server
+ cd /app/databag/net/server
+ nohup nice -n -5 /usr/local/go/bin/go run databag [dns name] &
+
+## Step 9: configure the server
+ Open your brower to https://[dns name]
+ Click the 'cog' in the upper right
+ Set an admin password
+ Select the 'cog' to bring up the settings modal
+ - set your hostname as [dns name]
+ - set the key to RSA 2048
+ - enable push notifications
+ - enable images
+ - disable audio
+ - disable video
+
+## Step 10: create accounts
+ Still in the admin dashboard
+ Click the 'add-user' button
+ Open the link in a new tab
+ Set a username and password
+ Setup your profile
+ Connect with contacts on other federated instances
+
+## Step 11: host for your friends and family
+ Back in the admin dashboard
+ Click the 'add-user' and send the link to anyone you want to host
+
+
diff --git a/net/container/compose.yaml b/net/container/compose.yaml
new file mode 100644
index 00000000..a1b33470
--- /dev/null
+++ b/net/container/compose.yaml
@@ -0,0 +1,10 @@
+version: "3.9"
+services:
+ databag:
+ container_name: databag
+ image: balzack/databag:latest
+ ports:
+ - "7000:7000"
+ volumes:
+ - ./databag-data:/data
+
diff --git a/net/server/main.go b/net/server/main.go
index 04f5570d..e914f69b 100644
--- a/net/server/main.go
+++ b/net/server/main.go
@@ -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 {