This is the official CKC base project, we use this for most of our new clients. It gives us:
❯ One command dev install: make
❯ Stack: Django + Vue + React Native
❯ Heroku deploys for frontend and backend
❯ Email templates + helpers
❯ Pre-commit with ruff
❯ Fast backend tests w/coverage (pytest)
❯ Hot reloading for local dev
❯ Expo for React Native
# Optionally set a skeletor branch to use something other than master
#export SKELETOR_BRANCH=some-feature-branch
bash <(curl -fsSL https://skeletor.ckcollab.com)
$ make
# (or make init)default login is "[email protected]" password "admin"
For local development (hot reloading, etc.): http://localhost:3000
To get to the django admin: http://localhost:8000/admin
$ make reset$ make test
# ..or with coverage:
# docker-compose exec django py.test --cov-report html:artifacts/coverag
# run frontend tests
# docker-compose exec frontend bun run testif you are only doing local development you shouldn't have to do any extra configuration.
for a deployment you'll want to edit .env with your secrets.
You can use django-extensions to generate a nice diagram of the current model structure.
# make sure graphviz installed
# mac: brew install graphviz
# linux: apt-get install graphviz-dev
# install requisite graph visualizer libs
docker-compose exec django pip install pygraphviz
# take the screenshot
docker-compose exec django ./manage.py graph_models -a -g -o my_project_visualized.pngwhen we deploy we'll do the following..
- rebuild the containers (in case of
requirements.txtorpackage.jsonchanges) - compile & collect static assets (vuejs)
- run migrations
$ make deployeg sending a welcome email....
$ docker-compose exec django ./manage.py email welcome [email protected]Run npm i inside of the src/mobile directory.
Populate a .env file with the following variables, so they get pulled into the device when running Expo Go:
BACKEND_URL=localhost:8000
If you want to run it on your device, you'll have to replace BACKEND_URL with your host computer's local IP address, so the phone can talk to the server.
Run npm run ios to automatically start up an iOS simulator with expo.
Change the BACKEND_URL environment variable in .env to your machine's local IP address (not localhost). This will allow your phone to know which server to send API requests to.
src/mobile/.env:
- BACKEND_URL=localhost:8000
# Example local ip
+ BACKEND_URL=192.168.1.133:8000Run npm run start to run expo without an iOS simulator. Although, npm run ios will work fine for this as well if you want both a physical device and a simulator running.
The app uses expo-router for file-based routing. This allows us to create screens without writing out all of the boiler plate for react navigation. The screens are separated into two main sections (auth) and (root) which are Groups from expo-router. The idea is to separate out screens that should be protected by auth from those that shouldn't be.
In order to protect the screens in the (root) group, the ones that should require the user to be authenticated, the app uses react context (in context/auth/provider.tsx). This context tracks auth state. Any time the user logs in or out or the route changes (navigation happened), this context will check if the user is in the right place. For example, if the user is logged out, but the route is somewhere in the (root) group, the app will redirect to the splash screen, the screen that appears when a user opens the app for the first time.
The user object inside of the auth context is persisted using AsyncStorage, so we know if the user is logged in next time the app starts.
The app uses token authentication via axios. axios is configured (in the requests.ts file) not to send cookies with the withCredentials: false setting, so that session auth (and csrf) doesn't intefere with the requests of the app.
When the user logs in, axios will take the token from the response and assign the Authorization header. It also saves it to AsyncStorage, so that when the app loads up next time, it loads the value into the axiosInstance. When the user logs out, the Authorization header is cleared along with the token in AsyncStorage.

