Compare commits

..

No commits in common. "2241b6f529f4703ca834412251fc7f2c3350cff4" and "81863ae912dee1b4d5ac8ce80c0dc08b6b613ab7" have entirely different histories.

16 changed files with 298 additions and 3651 deletions

View File

@ -1,17 +0,0 @@
#!/bin/bash
# Get the current timestamp in the format DD-MM-YY-TIME
timestamp=$(date +"%d-%m-%y-%H%M%S")
# Define the source file and the destination directory and file
src="data.db"
dst_dir="backups"
dst_file="data-${timestamp}.db"
# Make backups dir if necessary
mkdir -p $dst_dir
# Move and rename the file
cp "${src}" "${dst_dir}/${dst_file}"
echo "File has been moved to ${dst_dir} and renamed to ${dst_file}"

View File

@ -1,19 +0,0 @@
#!/bin/bash
echo 'Creating backup of database file...'
./data_backup.sh
echo 'Rebuilding server...'
sudo systemctl stop zerver
git pull
zig build
echo 'Restarting systemd...'
sudo systemctl restart zerver
systemctl status zerver

File diff suppressed because it is too large Load Diff

View File

@ -1,677 +0,0 @@
// -------------------------------------------------------------------------- //
// Copyright (c) 2019, Jairus Martin. //
// Distributed under the terms of the MIT License. //
// The full license is in the file LICENSE, distributed with this software. //
// -------------------------------------------------------------------------- //
const std = @import("std");
const Timezone = @import("datetime.zig").Timezone;
const create = Timezone.create;
// Timezones
pub const Africa = struct {
pub const Abidjan = create("Africa/Abidjan", 0);
pub const Accra = create("Africa/Accra", 0);
pub const Addis_Ababa = create("Africa/Addis_Ababa", 180);
pub const Algiers = create("Africa/Algiers", 60);
pub const Asmara = create("Africa/Asmara", 180);
pub const Bamako = create("Africa/Bamako", 0);
pub const Bangui = create("Africa/Bangui", 60);
pub const Banjul = create("Africa/Banjul", 0);
pub const Bissau = create("Africa/Bissau", 0);
pub const Blantyre = create("Africa/Blantyre", 120);
pub const Brazzaville = create("Africa/Brazzaville", 60);
pub const Bujumbura = create("Africa/Bujumbura", 120);
pub const Cairo = create("Africa/Cairo", 120);
pub const Casablanca = create("Africa/Casablanca", 60);
pub const Ceuta = create("Africa/Ceuta", 60);
pub const Conakry = create("Africa/Conakry", 0);
pub const Dakar = create("Africa/Dakar", 0);
pub const Dar_es_Salaam = create("Africa/Dar_es_Salaam", 180);
pub const Djibouti = create("Africa/Djibouti", 180);
pub const Douala = create("Africa/Douala", 60);
pub const El_Aaiun = create("Africa/El_Aaiun", 0);
pub const Freetown = create("Africa/Freetown", 0);
pub const Gaborone = create("Africa/Gaborone", 120);
pub const Harare = create("Africa/Harare", 120);
pub const Johannesburg = create("Africa/Johannesburg", 120);
pub const Juba = create("Africa/Juba", 180);
pub const Kampala = create("Africa/Kampala", 180);
pub const Khartoum = create("Africa/Khartoum", 120);
pub const Kigali = create("Africa/Kigali", 120);
pub const Kinshasa = create("Africa/Kinshasa", 60);
pub const Lagos = create("Africa/Lagos", 60);
pub const Libreville = create("Africa/Libreville", 60);
pub const Lome = create("Africa/Lome", 0);
pub const Luanda = create("Africa/Luanda", 60);
pub const Lubumbashi = create("Africa/Lubumbashi", 120);
pub const Lusaka = create("Africa/Lusaka", 120);
pub const Malabo = create("Africa/Malabo", 60);
pub const Maputo = create("Africa/Maputo", 120);
pub const Maseru = create("Africa/Maseru", 120);
pub const Mbabane = create("Africa/Mbabane", 120);
pub const Mogadishu = create("Africa/Mogadishu", 180);
pub const Monrovia = create("Africa/Monrovia", 0);
pub const Nairobi = create("Africa/Nairobi", 180);
pub const Ndjamena = create("Africa/Ndjamena", 60);
pub const Niamey = create("Africa/Niamey", 60);
pub const Nouakchott = create("Africa/Nouakchott", 0);
pub const Ouagadougou = create("Africa/Ouagadougou", 0);
pub const Porto_Novo = create("Africa/Porto-Novo", 60);
pub const Sao_Tome = create("Africa/Sao_Tome", 0);
pub const Timbuktu = create("Africa/Timbuktu", 0);
pub const Tripoli = create("Africa/Tripoli", 120);
pub const Tunis = create("Africa/Tunis", 60);
pub const Windhoek = create("Africa/Windhoek", 120);
};
pub const America = struct {
pub const Adak = create("America/Adak", -600);
pub const Anchorage = create("America/Anchorage", -540);
pub const Anguilla = create("America/Anguilla", -240);
pub const Antigua = create("America/Antigua", -240);
pub const Araguaina = create("America/Araguaina", -180);
pub const Argentina = struct {
pub const Buenos_Aires = create("America/Argentina/Buenos_Aires", -180);
pub const Catamarca = create("America/Argentina/Catamarca", -180);
pub const ComodRivadavia = create("America/Argentina/ComodRivadavia", -180);
pub const Cordoba = create("America/Argentina/Cordoba", -180);
pub const Jujuy = create("America/Argentina/Jujuy", -180);
pub const La_Rioja = create("America/Argentina/La_Rioja", -180);
pub const Mendoza = create("America/Argentina/Mendoza", -180);
pub const Rio_Gallegos = create("America/Argentina/Rio_Gallegos", -180);
pub const Salta = create("America/Argentina/Salta", -180);
pub const San_Juan = create("America/Argentina/San_Juan", -180);
pub const San_Luis = create("America/Argentina/San_Luis", -180);
pub const Tucuman = create("America/Argentina/Tucuman", -180);
pub const Ushuaia = create("America/Argentina/Ushuaia", -180);
};
pub const Aruba = create("America/Aruba", -240);
pub const Asuncion = create("America/Asuncion", -240);
pub const Atikokan = create("America/Atikokan", -300);
pub const Atka = create("America/Atka", -600);
pub const Bahia = create("America/Bahia", -180);
pub const Bahia_Banderas = create("America/Bahia_Banderas", -360);
pub const Barbados = create("America/Barbados", -240);
pub const Belem = create("America/Belem", -180);
pub const Belize = create("America/Belize", -360);
pub const Blanc_Sablon = create("America/Blanc-Sablon", -240);
pub const Boa_Vista = create("America/Boa_Vista", -240);
pub const Bogota = create("America/Bogota", -300);
pub const Boise = create("America/Boise", -420);
pub const Buenos_Aires = create("America/Buenos_Aires", -180);
pub const Cambridge_Bay = create("America/Cambridge_Bay", -420);
pub const Campo_Grande = create("America/Campo_Grande", -240);
pub const Cancun = create("America/Cancun", -300);
pub const Caracas = create("America/Caracas", -240);
pub const Catamarca = create("America/Catamarca", -180);
pub const Cayenne = create("America/Cayenne", -180);
pub const Cayman = create("America/Cayman", -300);
pub const Chicago = create("America/Chicago", -360);
pub const Chihuahua = create("America/Chihuahua", -420);
pub const Coral_Harbour = create("America/Coral_Harbour", -300);
pub const Cordoba = create("America/Cordoba", -180);
pub const Costa_Rica = create("America/Costa_Rica", -360);
pub const Creston = create("America/Creston", -420);
pub const Cuiaba = create("America/Cuiaba", -240);
pub const Curacao = create("America/Curacao", -240);
pub const Danmarkshavn = create("America/Danmarkshavn", 0);
pub const Dawson = create("America/Dawson", -480);
pub const Dawson_Creek = create("America/Dawson_Creek", -420);
pub const Denver = create("America/Denver", -420);
pub const Detroit = create("America/Detroit", -300);
pub const Dominica = create("America/Dominica", -240);
pub const Edmonton = create("America/Edmonton", -420);
pub const Eirunepe = create("America/Eirunepe", -300);
pub const El_Salvador = create("America/El_Salvador", -360);
pub const Ensenada = create("America/Ensenada", -480);
pub const Fort_Nelson = create("America/Fort_Nelson", -420);
pub const Fort_Wayne = create("America/Fort_Wayne", -300);
pub const Fortaleza = create("America/Fortaleza", -180);
pub const Glace_Bay = create("America/Glace_Bay", -240);
pub const Godthab = create("America/Godthab", -180);
pub const Goose_Bay = create("America/Goose_Bay", -240);
pub const Grand_Turk = create("America/Grand_Turk", -300);
pub const Grenada = create("America/Grenada", -240);
pub const Guadeloupe = create("America/Guadeloupe", -240);
pub const Guatemala = create("America/Guatemala", -360);
pub const Guayaquil = create("America/Guayaquil", -300);
pub const Guyana = create("America/Guyana", -240);
pub const Halifax = create("America/Halifax", -240);
pub const Havana = create("America/Havana", -300);
pub const Hermosillo = create("America/Hermosillo", -420);
pub const Indiana = struct {
// FIXME: Name conflict
pub const Indianapolis_ = create("America/Indiana/Indianapolis", -300);
pub const Knox = create("America/Indiana/Knox", -360);
pub const Marengo = create("America/Indiana/Marengo", -300);
pub const Petersburg = create("America/Indiana/Petersburg", -300);
pub const Tell_City = create("America/Indiana/Tell_City", -360);
pub const Vevay = create("America/Indiana/Vevay", -300);
pub const Vincennes = create("America/Indiana/Vincennes", -300);
pub const Winamac = create("America/Indiana/Winamac", -300);
};
pub const Indianapolis = create("America/Indianapolis", -300);
pub const Inuvik = create("America/Inuvik", -420);
pub const Iqaluit = create("America/Iqaluit", -300);
pub const Jamaica = create("America/Jamaica", -300);
pub const Jujuy = create("America/Jujuy", -180);
pub const Juneau = create("America/Juneau", -540);
pub const Kentucky = struct {
// FIXME: Name conflict
pub const Louisville_ = create("America/Kentucky/Louisville", -300);
pub const Monticello = create("America/Kentucky/Monticello", -300);
};
pub const Knox_IN = create("America/Knox_IN", -360);
pub const Kralendijk = create("America/Kralendijk", -240);
pub const La_Paz = create("America/La_Paz", -240);
pub const Lima = create("America/Lima", -300);
pub const Los_Angeles = create("America/Los_Angeles", -480);
pub const Louisville = create("America/Louisville", -300);
pub const Lower_Princes = create("America/Lower_Princes", -240);
pub const Maceio = create("America/Maceio", -180);
pub const Managua = create("America/Managua", -360);
pub const Manaus = create("America/Manaus", -240);
pub const Marigot = create("America/Marigot", -240);
pub const Martinique = create("America/Martinique", -240);
pub const Matamoros = create("America/Matamoros", -360);
pub const Mazatlan = create("America/Mazatlan", -420);
pub const Mendoza = create("America/Mendoza", -180);
pub const Menominee = create("America/Menominee", -360);
pub const Merida = create("America/Merida", -360);
pub const Metlakatla = create("America/Metlakatla", -540);
pub const Mexico_City = create("America/Mexico_City", -360);
pub const Miquelon = create("America/Miquelon", -180);
pub const Moncton = create("America/Moncton", -240);
pub const Monterrey = create("America/Monterrey", -360);
pub const Montevideo = create("America/Montevideo", -180);
pub const Montreal = create("America/Montreal", -300);
pub const Montserrat = create("America/Montserrat", -240);
pub const Nassau = create("America/Nassau", -300);
pub const New_York = create("America/New_York", -300);
pub const Nipigon = create("America/Nipigon", -300);
pub const Nome = create("America/Nome", -540);
pub const Noronha = create("America/Noronha", -120);
pub const North_Dakota = struct {
pub const Beulah = create("America/North_Dakota/Beulah", -360);
pub const Center = create("America/North_Dakota/Center", -360);
pub const New_Salem = create("America/North_Dakota/New_Salem", -360);
};
pub const Ojinaga = create("America/Ojinaga", -420);
pub const Panama = create("America/Panama", -300);
pub const Pangnirtung = create("America/Pangnirtung", -300);
pub const Paramaribo = create("America/Paramaribo", -180);
pub const Phoenix = create("America/Phoenix", -420);
pub const Port_of_Spain = create("America/Port_of_Spain", -240);
pub const Port_au_Prince = create("America/Port-au-Prince", -300);
pub const Porto_Acre = create("America/Porto_Acre", -300);
pub const Porto_Velho = create("America/Porto_Velho", -240);
pub const Puerto_Rico = create("America/Puerto_Rico", -240);
pub const Punta_Arenas = create("America/Punta_Arenas", -180);
pub const Rainy_River = create("America/Rainy_River", -360);
pub const Rankin_Inlet = create("America/Rankin_Inlet", -360);
pub const Recife = create("America/Recife", -180);
pub const Regina = create("America/Regina", -360);
pub const Resolute = create("America/Resolute", -360);
pub const Rio_Branco = create("America/Rio_Branco", -300);
pub const Rosario = create("America/Rosario", -180);
pub const Santa_Isabel = create("America/Santa_Isabel", -480);
pub const Santarem = create("America/Santarem", -180);
pub const Santiago = create("America/Santiago", -240);
pub const Santo_Domingo = create("America/Santo_Domingo", -240);
pub const Sao_Paulo = create("America/Sao_Paulo", -180);
pub const Scoresbysund = create("America/Scoresbysund", -60);
pub const Shiprock = create("America/Shiprock", -420);
pub const Sitka = create("America/Sitka", -540);
pub const St_Barthelemy = create("America/St_Barthelemy", -240);
pub const St_Johns = create("America/St_Johns", -210);
pub const St_Kitts = create("America/St_Kitts", -240);
pub const St_Lucia = create("America/St_Lucia", -240);
pub const St_Thomas = create("America/St_Thomas", -240);
pub const St_Vincent = create("America/St_Vincent", -240);
pub const Swift_Current = create("America/Swift_Current", -360);
pub const Tegucigalpa = create("America/Tegucigalpa", -360);
pub const Thule = create("America/Thule", -240);
pub const Thunder_Bay = create("America/Thunder_Bay", -300);
pub const Tijuana = create("America/Tijuana", -480);
pub const Toronto = create("America/Toronto", -300);
pub const Tortola = create("America/Tortola", -240);
pub const Vancouver = create("America/Vancouver", -480);
pub const Virgin = create("America/Virgin", -240);
pub const Whitehorse = create("America/Whitehorse", -480);
pub const Winnipeg = create("America/Winnipeg", -360);
pub const Yakutat = create("America/Yakutat", -540);
pub const Yellowknife = create("America/Yellowknife", -420);
};
pub const Antarctica = struct {
pub const Casey = create("Antarctica/Casey", 660);
pub const Davis = create("Antarctica/Davis", 420);
pub const DumontDUrville = create("Antarctica/DumontDUrville", 600);
pub const Macquarie = create("Antarctica/Macquarie", 660);
pub const Mawson = create("Antarctica/Mawson", 300);
pub const McMurdo = create("Antarctica/McMurdo", 720);
pub const Palmer = create("Antarctica/Palmer", -180);
pub const Rothera = create("Antarctica/Rothera", -180);
pub const South_Pole = create("Antarctica/South_Pole", 720);
pub const Syowa = create("Antarctica/Syowa", 180);
pub const Troll = create("Antarctica/Troll", 0);
pub const Vostok = create("Antarctica/Vostok", 360);
};
pub const Arctic = struct {
pub const Longyearbyen = create("Arctic/Longyearbyen", 60);
};
pub const Asia = struct {
pub const Aden = create("Asia/Aden", 180);
pub const Almaty = create("Asia/Almaty", 360);
pub const Amman = create("Asia/Amman", 120);
pub const Anadyr = create("Asia/Anadyr", 720);
pub const Aqtau = create("Asia/Aqtau", 300);
pub const Aqtobe = create("Asia/Aqtobe", 300);
pub const Ashgabat = create("Asia/Ashgabat", 300);
pub const Ashkhabad = create("Asia/Ashkhabad", 300);
pub const Atyrau = create("Asia/Atyrau", 300);
pub const Baghdad = create("Asia/Baghdad", 180);
pub const Bahrain = create("Asia/Bahrain", 180);
pub const Baku = create("Asia/Baku", 240);
pub const Bangkok = create("Asia/Bangkok", 420);
pub const Barnaul = create("Asia/Barnaul", 420);
pub const Beirut = create("Asia/Beirut", 120);
pub const Bishkek = create("Asia/Bishkek", 360);
pub const Brunei = create("Asia/Brunei", 480);
pub const Calcutta = create("Asia/Calcutta", 330);
pub const Chita = create("Asia/Chita", 540);
pub const Choibalsan = create("Asia/Choibalsan", 480);
pub const Chongqing = create("Asia/Chongqing", 480);
pub const Chungking = create("Asia/Chungking", 480);
pub const Colombo = create("Asia/Colombo", 330);
pub const Dacca = create("Asia/Dacca", 360);
pub const Damascus = create("Asia/Damascus", 120);
pub const Dhaka = create("Asia/Dhaka", 360);
pub const Dili = create("Asia/Dili", 540);
pub const Dubai = create("Asia/Dubai", 240);
pub const Dushanbe = create("Asia/Dushanbe", 300);
pub const Famagusta = create("Asia/Famagusta", 120);
pub const Gaza = create("Asia/Gaza", 120);
pub const Harbin = create("Asia/Harbin", 480);
pub const Hebron = create("Asia/Hebron", 120);
pub const Ho_Chi_Minh = create("Asia/Ho_Chi_Minh", 420);
pub const Hong_Kong = create("Asia/Hong_Kong", 480);
pub const Hovd = create("Asia/Hovd", 420);
pub const Irkutsk = create("Asia/Irkutsk", 480);
pub const Istanbul = create("Asia/Istanbul", 180);
pub const Jakarta = create("Asia/Jakarta", 420);
pub const Jayapura = create("Asia/Jayapura", 540);
pub const Jerusalem = create("Asia/Jerusalem", 120);
pub const Kabul = create("Asia/Kabul", 270);
pub const Kamchatka = create("Asia/Kamchatka", 720);
pub const Karachi = create("Asia/Karachi", 300);
pub const Kashgar = create("Asia/Kashgar", 360);
pub const Kathmandu = create("Asia/Kathmandu", 345);
pub const Katmandu = create("Asia/Katmandu", 345);
pub const Khandyga = create("Asia/Khandyga", 540);
pub const Kolkata = create("Asia/Kolkata", 330);
pub const Krasnoyarsk = create("Asia/Krasnoyarsk", 420);
pub const Kuala_Lumpur = create("Asia/Kuala_Lumpur", 480);
pub const Kuching = create("Asia/Kuching", 480);
pub const Kuwait = create("Asia/Kuwait", 180);
pub const Macao = create("Asia/Macao", 480);
pub const Macau = create("Asia/Macau", 480);
pub const Magadan = create("Asia/Magadan", 660);
pub const Makassar = create("Asia/Makassar", 480);
pub const Manila = create("Asia/Manila", 480);
pub const Muscat = create("Asia/Muscat", 240);
pub const Nicosia = create("Asia/Nicosia", 120);
pub const Novokuznetsk = create("Asia/Novokuznetsk", 420);
pub const Novosibirsk = create("Asia/Novosibirsk", 420);
pub const Omsk = create("Asia/Omsk", 360);
pub const Oral = create("Asia/Oral", 300);
pub const Phnom_Penh = create("Asia/Phnom_Penh", 420);
pub const Pontianak = create("Asia/Pontianak", 420);
pub const Pyongyang = create("Asia/Pyongyang", 540);
pub const Qatar = create("Asia/Qatar", 180);
pub const Qyzylorda = create("Asia/Qyzylorda", 300);
pub const Rangoon = create("Asia/Rangoon", 390);
pub const Riyadh = create("Asia/Riyadh", 180);
pub const Saigon = create("Asia/Saigon", 420);
pub const Sakhalin = create("Asia/Sakhalin", 660);
pub const Samarkand = create("Asia/Samarkand", 300);
pub const Seoul = create("Asia/Seoul", 540);
pub const Shanghai = create("Asia/Shanghai", 480);
pub const Singapore = create("Asia/Singapore", 480);
pub const Srednekolymsk = create("Asia/Srednekolymsk", 660);
pub const Taipei = create("Asia/Taipei", 480);
pub const Tashkent = create("Asia/Tashkent", 300);
pub const Tbilisi = create("Asia/Tbilisi", 240);
pub const Tehran = create("Asia/Tehran", 210);
pub const Tel_Aviv = create("Asia/Tel_Aviv", 120);
pub const Thimbu = create("Asia/Thimbu", 360);
pub const Thimphu = create("Asia/Thimphu", 360);
pub const Tokyo = create("Asia/Tokyo", 540);
pub const Tomsk = create("Asia/Tomsk", 420);
pub const Ujung_Pandang = create("Asia/Ujung_Pandang", 480);
pub const Ulaanbaatar = create("Asia/Ulaanbaatar", 480);
pub const Ulan_Bator = create("Asia/Ulan_Bator", 480);
pub const Urumqi = create("Asia/Urumqi", 360);
pub const Ust_Nera = create("Asia/Ust-Nera", 600);
pub const Vientiane = create("Asia/Vientiane", 420);
pub const Vladivostok = create("Asia/Vladivostok", 600);
pub const Yakutsk = create("Asia/Yakutsk", 540);
pub const Yangon = create("Asia/Yangon", 390);
pub const Yekaterinburg = create("Asia/Yekaterinburg", 300);
pub const Yerevan = create("Asia/Yerevan", 240);
};
pub const Atlantic = struct {
pub const Azores = create("Atlantic/Azores", -60);
pub const Bermuda = create("Atlantic/Bermuda", -240);
pub const Canary = create("Atlantic/Canary", 0);
pub const Cape_Verde = create("Atlantic/Cape_Verde", -60);
pub const Faeroe = create("Atlantic/Faeroe", 0);
pub const Faroe = create("Atlantic/Faroe", 0);
pub const Jan_Mayen = create("Atlantic/Jan_Mayen", 60);
pub const Madeira = create("Atlantic/Madeira", 0);
pub const Reykjavik = create("Atlantic/Reykjavik", 0);
pub const South_Georgia = create("Atlantic/South_Georgia", -120);
pub const St_Helena = create("Atlantic/St_Helena", 0);
pub const Stanley = create("Atlantic/Stanley", -180);
};
pub const Australia = struct {
pub const ACT = create("Australia/ACT", 600);
pub const Adelaide = create("Australia/Adelaide", 570);
pub const Brisbane = create("Australia/Brisbane", 600);
pub const Broken_Hill = create("Australia/Broken_Hill", 570);
pub const Canberra = create("Australia/Canberra", 600);
pub const Currie = create("Australia/Currie", 600);
pub const Darwin = create("Australia/Darwin", 570);
pub const Eucla = create("Australia/Eucla", 525);
pub const Hobart = create("Australia/Hobart", 600);
pub const LHI = create("Australia/LHI", 630);
pub const Lindeman = create("Australia/Lindeman", 600);
pub const Lord_Howe = create("Australia/Lord_Howe", 630);
pub const Melbourne = create("Australia/Melbourne", 600);
pub const North = create("Australia/North", 570);
pub const NSW = create("Australia/NSW", 600);
pub const Perth = create("Australia/Perth", 480);
pub const Queensland = create("Australia/Queensland", 600);
pub const South = create("Australia/South", 570);
pub const Sydney = create("Australia/Sydney", 600);
pub const Tasmania = create("Australia/Tasmania", 600);
pub const Victoria = create("Australia/Victoria", 600);
pub const West = create("Australia/West", 480);
pub const Yancowinna = create("Australia/Yancowinna", 570);
};
pub const Brazil = struct {
pub const Acre = create("Brazil/Acre", -300);
pub const DeNoronha = create("Brazil/DeNoronha", -120);
pub const East = create("Brazil/East", -180);
pub const West = create("Brazil/West", -240);
};
pub const Canada = struct {
pub const Atlantic = create("Canada/Atlantic", -240);
pub const Central = create("Canada/Central", -360);
pub const Eastern = create("Canada/Eastern", -300);
pub const Mountain = create("Canada/Mountain", -420);
pub const Newfoundland = create("Canada/Newfoundland", -210);
pub const Pacific = create("Canada/Pacific", -480);
pub const Saskatchewan = create("Canada/Saskatchewan", -360);
pub const Yukon = create("Canada/Yukon", -480);
};
pub const CET = create("CET", 60);
pub const Chile = struct {
pub const Continental = create("Chile/Continental", -240);
pub const EasterIsland = create("Chile/EasterIsland", -360);
};
pub const CST6CDT = create("CST6CDT", -360);
pub const Cuba = create("Cuba", -300);
pub const EET = create("EET", 120);
pub const Egypt = create("Egypt", 120);
pub const Eire = create("Eire", 0);
pub const EST = create("EST", -300);
pub const EST5EDT = create("EST5EDT", -300);
pub const Etc = struct {
// NOTE: The signs are intentionally inverted. See the Etc area description.
pub const GMT = create("Etc/GMT", 0);
pub const GMTp0 = create("Etc/GMT+0", 0);
pub const GMTp1 = create("Etc/GMT+1", -60);
pub const GMTp10 = create("Etc/GMT+10", -600);
pub const GMTp11 = create("Etc/GMT+11", -660);
pub const GMTp12 = create("Etc/GMT+12", -720);
pub const GMTp2 = create("Etc/GMT+2", -120);
pub const GMTp3 = create("Etc/GMT+3", -180);
pub const GMTp4 = create("Etc/GMT+4", -240);
pub const GMTp5 = create("Etc/GMT+5", -300);
pub const GMTp6 = create("Etc/GMT+6", -360);
pub const GMTp7 = create("Etc/GMT+7", -420);
pub const GMTp8 = create("Etc/GMT+8", -480);
pub const GMTp9 = create("Etc/GMT+9", -540);
pub const GMT0 = create("Etc/GMT0", 0);
pub const GMTm0 = create("Etc/GMT-0", 0);
pub const GMTm1 = create("Etc/GMT-1", 60);
pub const GMTm10 = create("Etc/GMT-10", 600);
pub const GMTm11 = create("Etc/GMT-11", 660);
pub const GMTm12 = create("Etc/GMT-12", 720);
pub const GMTm13 = create("Etc/GMT-13", 780);
pub const GMTm14 = create("Etc/GMT-14", 840);
pub const GMTm2 = create("Etc/GMT-2", 120);
pub const GMTm3 = create("Etc/GMT-3", 180);
pub const GMTm4 = create("Etc/GMT-4", 240);
pub const GMTm5 = create("Etc/GMT-5", 300);
pub const GMTm6 = create("Etc/GMT-6", 360);
pub const GMTm7 = create("Etc/GMT-7", 420);
pub const GMTm8 = create("Etc/GMT-8", 480);
pub const GMTm9 = create("Etc/GMT-9", 540);
pub const Greenwich = create("Etc/Greenwich", 0);
pub const UCT = create("Etc/UCT", 0);
pub const Universal = create("Etc/Universal", 0);
pub const UTC = create("Etc/UTC", 0);
pub const Zulu = create("Etc/Zulu", 0);
};
pub const Europe = struct {
pub const Amsterdam = create("Europe/Amsterdam", 60);
pub const Andorra = create("Europe/Andorra", 60);
pub const Astrakhan = create("Europe/Astrakhan", 240);
pub const Athens = create("Europe/Athens", 120);
pub const Belfast = create("Europe/Belfast", 0);
pub const Belgrade = create("Europe/Belgrade", 60);
pub const Berlin = create("Europe/Berlin", 60);
pub const Bratislava = create("Europe/Bratislava", 60);
pub const Brussels = create("Europe/Brussels", 60);
pub const Bucharest = create("Europe/Bucharest", 120);
pub const Budapest = create("Europe/Budapest", 60);
pub const Busingen = create("Europe/Busingen", 60);
pub const Chisinau = create("Europe/Chisinau", 120);
pub const Copenhagen = create("Europe/Copenhagen", 60);
pub const Dublin = create("Europe/Dublin", 0);
pub const Gibraltar = create("Europe/Gibraltar", 60);
pub const Guernsey = create("Europe/Guernsey", 0);
pub const Helsinki = create("Europe/Helsinki", 120);
pub const Isle_of_Man = create("Europe/Isle_of_Man", 0);
pub const Istanbul = create("Europe/Istanbul", 180);
pub const Jersey = create("Europe/Jersey", 0);
pub const Kaliningrad = create("Europe/Kaliningrad", 120);
pub const Kiev = create("Europe/Kiev", 120);
pub const Kirov = create("Europe/Kirov", 180);
pub const Lisbon = create("Europe/Lisbon", 0);
pub const Ljubljana = create("Europe/Ljubljana", 60);
pub const London = create("Europe/London", 0);
pub const Luxembourg = create("Europe/Luxembourg", 60);
pub const Madrid = create("Europe/Madrid", 60);
pub const Malta = create("Europe/Malta", 60);
pub const Mariehamn = create("Europe/Mariehamn", 120);
pub const Minsk = create("Europe/Minsk", 180);
pub const Monaco = create("Europe/Monaco", 60);
pub const Moscow = create("Europe/Moscow", 180);
pub const Oslo = create("Europe/Oslo", 60);
pub const Paris = create("Europe/Paris", 60);
pub const Podgorica = create("Europe/Podgorica", 60);
pub const Prague = create("Europe/Prague", 60);
pub const Riga = create("Europe/Riga", 120);
pub const Rome = create("Europe/Rome", 60);
pub const Samara = create("Europe/Samara", 240);
pub const San_Marino = create("Europe/San_Marino", 60);
pub const Sarajevo = create("Europe/Sarajevo", 60);
pub const Saratov = create("Europe/Saratov", 240);
pub const Simferopol = create("Europe/Simferopol", 180);
pub const Skopje = create("Europe/Skopje", 60);
pub const Sofia = create("Europe/Sofia", 120);
pub const Stockholm = create("Europe/Stockholm", 60);
pub const Tallinn = create("Europe/Tallinn", 120);
pub const Tirane = create("Europe/Tirane", 60);
pub const Tiraspol = create("Europe/Tiraspol", 120);
pub const Ulyanovsk = create("Europe/Ulyanovsk", 240);
pub const Uzhgorod = create("Europe/Uzhgorod", 120);
pub const Vaduz = create("Europe/Vaduz", 60);
pub const Vatican = create("Europe/Vatican", 60);
pub const Vienna = create("Europe/Vienna", 60);
pub const Vilnius = create("Europe/Vilnius", 120);
pub const Volgograd = create("Europe/Volgograd", 240);
pub const Warsaw = create("Europe/Warsaw", 60);
pub const Zagreb = create("Europe/Zagreb", 60);
pub const Zaporozhye = create("Europe/Zaporozhye", 120);
pub const Zurich = create("Europe/Zurich", 60);
};
pub const GB = create("GB", 0);
pub const GB_Eire = create("GB-Eire", 0);
pub const GMT = create("GMT", 0);
pub const GMTp0 = create("GMT+0", 0);
pub const GMT0 = create("GMT0", 0);
pub const GMTm0 = create("GMT-0", 0);
pub const Greenwich = create("Greenwich", 0);
pub const Hongkong = create("Hongkong", 480);
pub const HST = create("HST", -600);
pub const Iceland = create("Iceland", 0);
pub const Indian = struct {
pub const Antananarivo = create("Indian/Antananarivo", 180);
pub const Chagos = create("Indian/Chagos", 360);
pub const Christmas = create("Indian/Christmas", 420);
pub const Cocos = create("Indian/Cocos", 390);
pub const Comoro = create("Indian/Comoro", 180);
pub const Kerguelen = create("Indian/Kerguelen", 300);
pub const Mahe = create("Indian/Mahe", 240);
pub const Maldives = create("Indian/Maldives", 300);
pub const Mauritius = create("Indian/Mauritius", 240);
pub const Mayotte = create("Indian/Mayotte", 180);
pub const Reunion = create("Indian/Reunion", 240);
};
pub const Iran = create("Iran", 210);
pub const Israel = create("Israel", 120);
pub const Jamaica = create("Jamaica", -300);
pub const Japan = create("Japan", 540);
pub const Kwajalein = create("Kwajalein", 720);
pub const Libya = create("Libya", 120);
pub const MET = create("MET", 60);
pub const Mexico = struct {
pub const BajaNorte = create("Mexico/BajaNorte", -480);
pub const BajaSur = create("Mexico/BajaSur", -420);
pub const General = create("Mexico/General", -360);
};
pub const MST = create("MST", -420);
pub const MST7MDT = create("MST7MDT", -420);
pub const Navajo = create("Navajo", -420);
pub const NZ = create("NZ", 720);
pub const NZ_CHAT = create("NZ-CHAT", 765);
pub const Pacific = struct {
pub const Apia = create("Pacific/Apia", 780);
pub const Auckland = create("Pacific/Auckland", 720);
pub const Bougainville = create("Pacific/Bougainville", 660);
pub const Chatham = create("Pacific/Chatham", 765);
pub const Chuuk = create("Pacific/Chuuk", 600);
pub const Easter = create("Pacific/Easter", -360);
pub const Efate = create("Pacific/Efate", 660);
pub const Enderbury = create("Pacific/Enderbury", 780);
pub const Fakaofo = create("Pacific/Fakaofo", 780);
pub const Fiji = create("Pacific/Fiji", 720);
pub const Funafuti = create("Pacific/Funafuti", 720);
pub const Galapagos = create("Pacific/Galapagos", -360);
pub const Gambier = create("Pacific/Gambier", -540);
pub const Guadalcanal = create("Pacific/Guadalcanal", 660);
pub const Guam = create("Pacific/Guam", 600);
pub const Honolulu = create("Pacific/Honolulu", -600);
pub const Johnston = create("Pacific/Johnston", -600);
pub const Kiritimati = create("Pacific/Kiritimati", 840);
pub const Kosrae = create("Pacific/Kosrae", 660);
pub const Kwajalein = create("Pacific/Kwajalein", 720);
pub const Majuro = create("Pacific/Majuro", 720);
pub const Marquesas = create("Pacific/Marquesas", -570);
pub const Midway = create("Pacific/Midway", -660);
pub const Nauru = create("Pacific/Nauru", 720);
pub const Niue = create("Pacific/Niue", -660);
pub const Norfolk = create("Pacific/Norfolk", 660);
pub const Noumea = create("Pacific/Noumea", 660);
pub const Pago_Pago = create("Pacific/Pago_Pago", -660);
pub const Palau = create("Pacific/Palau", 540);
pub const Pitcairn = create("Pacific/Pitcairn", -480);
pub const Pohnpei = create("Pacific/Pohnpei", 660);
pub const Ponape = create("Pacific/Ponape", 660);
pub const Port_Moresby = create("Pacific/Port_Moresby", 600);
pub const Rarotonga = create("Pacific/Rarotonga", -600);
pub const Saipan = create("Pacific/Saipan", 600);
pub const Samoa = create("Pacific/Samoa", -660);
pub const Tahiti = create("Pacific/Tahiti", -600);
pub const Tarawa = create("Pacific/Tarawa", 720);
pub const Tongatapu = create("Pacific/Tongatapu", 780);
pub const Truk = create("Pacific/Truk", 600);
pub const Wake = create("Pacific/Wake", 720);
pub const Wallis = create("Pacific/Wallis", 720);
pub const Yap = create("Pacific/Yap", 600);
};
pub const Poland = create("Poland", 60);
pub const Portugal = create("Portugal", 0);
pub const PRC = create("PRC", 480);
pub const PST8PDT = create("PST8PDT", -480);
pub const ROC = create("ROC", 480);
pub const ROK = create("ROK", 540);
pub const Singapore = create("Singapore", 480);
pub const Turkey = create("Turkey", 180);
pub const UCT = create("UCT", 0);
pub const Universal = create("Universal", 0);
pub const US = struct {
pub const Alaska = create("US/Alaska", -540);
pub const Aleutian = create("US/Aleutian", -600);
pub const Arizona = create("US/Arizona", -420);
pub const Central = create("US/Central", -360);
pub const Eastern = create("US/Eastern", -300);
pub const East_Indiana = create("US/East-Indiana", -300);
pub const Hawaii = create("US/Hawaii", -600);
pub const Indiana_Starke = create("US/Indiana-Starke", -360);
pub const Michigan = create("US/Michigan", -300);
pub const Mountain = create("US/Mountain", -420);
pub const Pacific = create("US/Pacific", -480);
pub const Pacific_New = create("US/Pacific-New", -480);
pub const Samoa = create("US/Samoa", -660);
};
pub const UTC = create("UTC", 0);
pub const WET = create("WET", 0);
pub const W_SU = create("W-SU", 180);
pub const Zulu = create("Zulu", 0);
// TODO: Allow lookup by name
//pub fn getAll() []*const Timezone {
// for (comptime std.meta.fields(@This())) |field {
//
// }
//}
//pub fn get(name: []const u8) ?*const Timezone {
// return ALL_TIMEZONES.getValue(name);
//}
test "timezone-get" {
const testing = std.testing;
//try testing.expect(get("America/New_York").? == America.New_York);
try testing.expect(America.New_York.offset == -300);
}

View File

@ -11,11 +11,9 @@ pub const Db = struct {
_mode: ?sqlite.Db.Mode,
_sql_db: sqlite.Db,
pub fn init(allocator: Allocator, db_path: ?[]const u8, mode: ?sqlite.Db.Mode) !Db {
const path: [:0]const u8 = if (db_path != null) try std.mem.Allocator.dupeZ(allocator, u8, db_path.?) else "./data.db";
defer allocator.free(path);
pub fn init(allocator: Allocator, mode: ?sqlite.Db.Mode) !Db {
var sqlDb = try sqlite.Db.init(.{
.mode = if (mode != null) mode.? else sqlite.Db.Mode{ .File = path },
.mode = if (mode != null) mode.? else sqlite.Db.Mode{ .File = "./data.db" },
.open_flags = .{
.write = true,
.create = true,
@ -33,85 +31,21 @@ pub const Db = struct {
self: *Db,
comptime Type: type,
allocator: Allocator,
comptime where_clause: []const u8,
comptime whereClause: []const u8,
values: anytype,
comptime order_by_field: ?[]const u8,
comptime order: ?[]const u8,
comptime limit: ?bool,
limit_val: ?u32,
comptime limit: ?u32,
) !?[]Type {
comptime {
if (order == null and order_by_field != null or order != null and order_by_field == null) {
@compileError("Must provide both order and order_by or neither, with select " ++ where_clause);
}
if (order != null) {
if (!(std.mem.eql(u8, order.?, "DESC") or std.mem.eql(u8, order.?, "ASC"))) {
@compileError("Must use ASC or DESC for order_by, used: " ++ order.?);
}
}
if (!std.mem.containsAtLeast(u8, where_clause, 1, "?")) {
@compileError("where_clause missing '?', no possible values to insert " ++ where_clause);
}
// Check that where_clause only contains fields in struct
var query_objs_iter = std.mem.split(u8, where_clause, "?");
inline for (@typeInfo(@TypeOf(values)).Struct.fields) |struct_field| {
const name = struct_field.name;
const query_obj = query_objs_iter.next();
if (query_obj == null) {
@compileError("Query does not have enough clauses for passed in data: " ++ where_clause);
}
if (std.mem.containsAtLeast(u8, query_obj.?, 1, name)) {
continue;
} else {
@compileError("Missing field or messed up order in select:\n" ++ where_clause ++ "\n" ++ query_obj.?);
}
}
const last = query_objs_iter.next();
if (last != null and !std.mem.eql(u8, last.?, "")) {
@compileError("Values is lacking, query contains more ? than data provided: " ++ where_clause ++ "\nLeft with: " ++ last.?);
}
}
if (limit == null and limit_val != null or limit != null and limit_val == null) {
std.log.err("Must provide both limit and limit_val or neither, with select: {s}", .{where_clause});
return null;
}
_ = limit;
var res_array: std.ArrayList(Type) = std.ArrayList(Type).init(allocator);
if (order_by_field == null and limit == null) {
const query = "SELECT * FROM " ++ models.getTypeTableName(Type) ++ " " ++ where_clause ++ ";";
var stmt = try self._sql_db.prepare(query);
defer stmt.deinit();
var iter = try stmt.iteratorAlloc(Type, allocator, values);
while (try iter.nextAlloc(allocator, .{})) |row| {
try res_array.append(row);
}
} else if (order_by_field == null and limit != null) {
const query = "SELECT * FROM " ++ models.getTypeTableName(Type) ++ " " ++ where_clause ++ " LIMIT ?;";
var stmt = try self._sql_db.prepare(query);
defer stmt.deinit();
const query = "SELECT * FROM " ++ models.getTypeTableName(Type) ++ " " ++ whereClause ++ ";";
var stmt = try self._sql_db.prepare(query);
defer stmt.deinit();
var iter = try stmt.iteratorAlloc(Type, allocator, utils.structConcatFields(values, .{ .limit = limit_val.? }));
while (try iter.nextAlloc(allocator, .{})) |row| {
try res_array.append(row);
}
} else if (order_by_field != null and limit == null) {
const query = "SELECT * FROM " ++ models.getTypeTableName(Type) ++ " " ++ where_clause ++ " ORDER BY " ++ order_by_field.? ++ " " ++ order.? ++ ";";
var stmt = try self._sql_db.prepare(query);
defer stmt.deinit();
var iter = try stmt.iteratorAlloc(Type, allocator, values);
while (try iter.nextAlloc(allocator, .{})) |row| {
try res_array.append(row);
}
} else {
const query = "SELECT * FROM " ++ models.getTypeTableName(Type) ++ " " ++ where_clause ++ " ORDER BY " ++ order_by_field.? ++ " " ++ order.? ++ " LIMIT ?;";
var stmt = try self._sql_db.prepare(query);
defer stmt.deinit();
var iter = try stmt.iteratorAlloc(Type, allocator, utils.structConcatFields(values, .{ .limit = limit_val.? }));
while (try iter.nextAlloc(allocator, .{})) |row| {
try res_array.append(row);
}
var iter = try stmt.iteratorAlloc(Type, allocator, values);
while (try iter.nextAlloc(allocator, .{})) |row| {
try res_array.append(row);
}
return try res_array.toOwnedSlice();
@ -123,21 +57,6 @@ pub const Db = struct {
}
pub fn selectOne(self: *Db, comptime Type: type, allocator: Allocator, comptime query: []const u8, values: anytype) !?Type {
comptime {
var query_objs_iter = std.mem.split(u8, query, "=");
inline for (@typeInfo(@TypeOf(values)).Struct.fields) |struct_field| {
const name = struct_field.name;
const query_obj = query_objs_iter.next();
if (query_obj == null) {
@compileError("Query does not have enough clauses for passed in data:\n" ++ "Type: " ++ @typeName(Type) ++ "\n" ++ query ++ "\n");
}
if (std.mem.containsAtLeast(u8, query_obj.?, 1, name)) {
continue;
} else {
@compileError("Missing field or messed up order in select:\n" ++ query ++ "\n" ++ query_obj.?);
}
}
}
const row = try self._sql_db.oneAlloc(Type, allocator, query, .{}, values);
// std.debug.print("{any}", .{row});
return row;
@ -152,35 +71,10 @@ pub const Db = struct {
};
}
pub fn updateHideById(self: *Db, comptime Type: type, comptime hide: bool, id: u32) !void {
const hide_val = if (hide) 1 else 0;
const now = @intCast(u64, std.time.milliTimestamp());
self._sql_db.exec(models.createHideQuery(Type), .{}, .{ .hide = hide_val, .updated_at = now, .id = id }) catch |err| {
std.log.err("Encountered error while updating hide on model {s}", .{@typeName(Type)});
return err;
};
}
pub fn insert(self: *Db, comptime Type: type, values: anytype) !void {
comptime {
const query = models.createInsertQuery(Type);
// const InsertType = utils.removeStructFields(Type, &[_]u8{0});
var query_objs_iter = std.mem.split(u8, query, ",");
inline for (@typeInfo(@TypeOf(values)).Struct.fields) |struct_field| {
const name = struct_field.name;
const query_obj = query_objs_iter.next();
if (query_obj == null) {
@compileError("Query does not have enough clauses for passed in data:\n" ++ "Type: " ++ @typeName(Type) ++ "\n" ++ query ++ "\n");
}
if (std.mem.containsAtLeast(u8, query_obj.?, 1, name)) {
continue;
} else {
@compileError("Missing field or messed up order in insert:\n" ++ query ++ "\n" ++ query_obj.? ++ "\n");
}
}
}
// TODO check there is an ID field
self._sql_db.exec(models.createInsertQuery(Type), .{}, values) catch |err| {
std.debug.print("Encountered error while inserting data:\n\t{any}\nQuery:\t{s}\n{any}\n", .{ values, models.createInsertQuery(Type), err });
std.debug.print("Encountered error while inserting data:\n{any}\n\tQuery:{s}\n{any}\n", .{ values, models.createInsertQuery(Type), err });
return err;
};
}

View File

@ -1,31 +1,5 @@
const std = @import("std");
// fn UniqueSQLType(comptime ValType: type) type {
// comptime {
// _ = switch (@typeInfo(ValType)) {
// .Int, .Float, .Pointer => true,
// else => {
// @compileError("An invalid type passed to unique sql contraint type: " ++ @typeName(ValType));
// },
// };
// }
// comptime {
// const t = switch (@typeInfo(ValType)) {
// .Int => @Type(.{ .Int = .{ .signedness = @typeInfo(ValType).Int.signedness, .bits = @typeInfo(ValType).Int.bits } }),
// .Float => @Type(.{ .Float = .{ .bits = @typeInfo(ValType).Float.bits } }),
// .Pointer => {
// const pt = @typeInfo(ValType).Pointer;
// // _ = std.fmt.comptimePrint("Pointer type info: {any}", .{pt});
// return @Type(.{ .Pointer = .{ .size = pt.size, .is_const = pt.is_const, .is_volatile = pt.is_volatile, .alignment = pt.alignment, .address_space = pt.address_space, .child = pt.child, .is_allowzero = pt.is_allowzero, .sentinel = pt.sentinel } });
// },
// else => unreachable,
// };
// return t;
// }
// // @setEvalBranchQuota(additional_fields.len * fields.len * 10);
// }
// pub const UniqueType = union(enum) { string: []const u8, real: f64 };
const utils = @import("../utils.zig");
pub const Transaction = struct {
id: u32,
@ -33,7 +7,7 @@ pub const Transaction = struct {
type: []const u8,
memo: ?[]const u8,
budget_id: u32,
created_by_user_id: u32,
added_by_user_id: u32,
budget_category_id: ?u32,
date: u64,
created_at: u64,
@ -54,20 +28,15 @@ pub const BudgetCategory = struct {
pub const Budget = struct {
id: u32,
family_id: u32,
name: []const u8,
created_at: u64,
updated_at: u64,
hide: u8,
expected_income: ?f64,
};
pub const User = struct {
id: u32,
name: []const u8,
username: ?[]const u8,
email: ?[]const u8,
pass_hash: u32,
family_id: u32,
budget_id: u32,
created_at: u64,
@ -78,45 +47,13 @@ pub const User = struct {
pub const Family = struct {
id: u32,
code: ?[]const u8,
budget_id: u32,
hide: u8,
created_at: u64,
updated_at: u64,
hide: u8,
};
pub const SharedNote = struct {
id: u32,
family_id: u32,
created_by_user_id: u32,
content: []const u8,
title: []const u8,
color: ?[]const u8,
tag_ids: []const u8,
is_markdown: u2,
created_at: u64,
updated_at: u64,
hide: u8,
};
pub const Tag = struct {
id: u32,
family_id: u32,
created_by_user_id: u32,
name: []const u8,
type: []const u8,
created_at: u64,
updated_at: u64,
hide: u8,
};
pub const Token = struct {
user_id: u32,
family_id: u32,
generated_at: u64,
expires_at: u64,
};
pub const ModelTypes = [_]type{ Transaction, BudgetCategory, Budget, User, Family, SharedNote, Tag };
pub const ModelTypes = [5]type{ Transaction, BudgetCategory, Budget, User, Family };
/// Functions for creating SQLite queries for any models above
pub inline fn createSelectOnIdQuery(comptime Type: type) []const u8 {
@ -130,11 +67,7 @@ pub inline fn createSelectOnFieldQuery(
comptime comparator: []const u8,
) ![]const u8 {
comptime {
if (fieldName == null and structField == null) {
@compileError("Must provide struct and fieldname");
} else if (fieldName != null and structField != null) {
@compileError("Cannot provide struct and fieldname");
}
try std.testing.expect(fieldName == null and structField != null or fieldName != null and structField == null);
var field: []const u8 = undefined;
if (structField != null) {
field = structField.?;
@ -146,19 +79,6 @@ pub inline fn createSelectOnFieldQuery(
}
}
pub inline fn createRawSelectQuery(comptime Type: type, comptime where_query: []const u8) ![]const u8 {
comptime {
if (!std.mem.containsAtLeast(u8, where_query, 1, "WHERE")) {
@compileError("Provided where query does not contain 'WHERE' string: " ++ where_query);
}
if (!std.mem.endsWith(u8, where_query, ";")) {
@compileError("Where query does not end with semicolon: " ++ where_query);
}
var query = "SELECT * FROM " ++ getTypeTableName(Type) ++ " " ++ where_query;
return query;
}
}
pub inline fn createDeleteOnIdQuery(comptime Type: type) []const u8 {
return "DELETE from " ++ getTypeTableName(Type) ++ " WHERE id = ?;";
}
@ -169,14 +89,13 @@ pub inline fn createInsertQuery(comptime Type: type) []const u8 {
var qs: []const u8 = "?";
inline for (@typeInfo(Type).Struct.fields, 0..) |field, i| {
// This is brittle, assumes 'id' struct field is first
if (std.mem.eql(u8, field.name, "id")) {
continue;
}
if (i > 1) {
query = query ++ ", ";
qs = qs ++ ", ?";
}
query = query ++ field.name;
if (i != 0) {
query = query ++ field.name;
}
}
query = query ++ ") VALUES (" ++ qs ++ ");";
return query;
@ -197,23 +116,6 @@ pub inline fn createUpdateQuery(comptime Type: type) []const u8 {
}
}
pub fn createHideQuery(comptime Type: type) []const u8 {
comptime {
var has_hide = false;
inline for (@typeInfo(Type).Struct.fields) |field| {
if (std.mem.eql(u8, field.name, "hide")) {
has_hide = true;
}
}
if (!has_hide) {
@compileError("Model does not have hide field, cannot create hide query: " ++ @typeName(Type));
}
return "UPDATE " ++ getTypeTableName(Type) ++ " SET hide = ?, updated_at = ? WHERE id = ?;";
}
}
pub inline fn createTableDeleteQuery(comptime Type: type) []const u8 {
return "DROP TABLE IF EXISTS " ++ getTypeTableName(Type) ++ ";";
}
@ -237,11 +139,6 @@ pub inline fn createTableMigrationQuery(comptime Type: type) []const u8 {
inline fn getSQLiteColumnMigrateText(comptime struct_field: std.builtin.Type.StructField) []const u8 {
comptime {
if (std.mem.eql(u8, struct_field.name, "id")) return "INTEGER PRIMARY KEY AUTOINCREMENT";
if (std.mem.eql(u8, @typeName(struct_field.type), "UniqueSQLType")) {
@compileLog(struct_field.type);
}
// _ = std.fmt.comptimePrint("Got type {any}", .{@typeInfo(struct_field.type)});
const val = switch (@typeInfo(struct_field.type)) {
.Int => "INTEGER NOT NULL",
.Float => "REAL NOT NULL",
@ -254,14 +151,7 @@ inline fn getSQLiteColumnMigrateText(comptime struct_field: std.builtin.Type.Str
},
.Array => "TEXT NOT NULL",
.Pointer => "TEXT NOT NULL",
// .Struct => {
// if (std.mem.eql(u8, @typeName(struct_field.type), "UniqueSQLString") or std.mem.eql(u8, @typeName(struct_field.type), "UniqueSQLString"))
// return "THIS";
// },
else => {
@compileError("Passed in a type that has no sql migration defined: " ++ @typeName(struct_field.type));
},
else => unreachable,
};
return val;
}
@ -275,14 +165,7 @@ pub inline fn getTypeTableName(comptime Type: type) []const u8 {
Budget => "budgets",
BudgetCategory => "budget_categories",
Family => "families",
SharedNote => "shared_notes",
Tag => "tags",
// Tag => "tags",
else => {
@compileError("Missing a table name, check to make sure that each model in the models list has a table name provided here.\n" ++ @typeName(Type));
// _ = std.fmt.comptimePrint("Missing a table name, check to make sure that each model in the models list has a table name provided here.\n{any}", .{ModelTypes});
// unreachable;
},
else => unreachable,
};
}
}

View File

@ -6,55 +6,24 @@ const ztime = @import(".deps/time.zig");
const utils = @import("utils.zig");
const budget = @import("routes/budget.zig");
const auth = @import("routes/auth.zig");
const user = @import("routes/user.zig");
const trans = @import("routes/transactions.zig");
const dash = @import("routes/dashboard.zig");
const note = @import("routes/shared_note.zig");
const Db = @import("db/db.zig").Db;
var db: ?Db = null;
var db: Db = undefined;
pub fn getDb() *Db {
return &db.?;
return &db;
}
pub fn startHttpServer() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// db = try Db.init(allocator, null);
// defer db.deinit();
var args = try std.process.argsWithAllocator(allocator);
// skip program name
_ = args.skip();
while (args.next()) |arg| {
if (std.mem.eql(u8, arg, "--db_path")) {
const path = args.next();
// std.debug.print("Got path: {any}", .{path});
if (path) |db_path| {
db = try Db.init(allocator, db_path, null);
} else {
std.log.err("Db path not provided after arg", .{});
return;
}
}
if (std.mem.eql(u8, arg, "--make-migration")) {
if (db == null) {
std.log.err("Cannot migrate, provide db path first", .{});
return;
}
try db.?.wipeAndMigrateDb();
}
}
if (db == null) {
db = try Db.init(allocator, null, null);
}
defer db.?.deinit();
db = try Db.init(allocator, null);
defer db.deinit();
var server = try httpz.Server().init(allocator, .{ .port = 8081 });
@ -66,30 +35,22 @@ pub fn startHttpServer() !void {
var router = server.router();
router.post("/auth/login", user.login);
router.post("/auth/signup", user.signup);
// router.get("/user/:id", user.getUser);
router.get("/user/:id", user.getUser);
router.put("/user", user.putUser);
// router.delete("/user/:id", user.deleteUser);
router.delete("/user/:id", user.deleteUser);
router.get("/shared_note/:limit", note.getSharedNotes);
router.put("/shared_note", note.putSharedNote);
router.post("/shared_note", note.postSharedNote);
router.get("/budget/:id", budget.getBudget);
router.put("/budget", budget.putBudget);
router.post("/budget", budget.postBudget);
// router.get("/budget/:id", budget.getBudget);
// router.put("/budget", budget.putBudget);
// router.post("/budget", budget.postBudget);
router.post("/budget_category", budget.postBudgetCategory);
router.put("/budget_category", budget.putBudgetCategory);
router.delete("/budget_category", budget.deleteBudgetCategory);
router.post("/budget_category", budget.postBudgetCategory);
router.post("/transaction", trans.postTransaction);
router.put("/transaction", trans.putTransaction);
router.delete("/transaction", trans.deleteTransaction);
router.get("/transactions/:budget_id", trans.getTransactions);
router.post("/transactions", trans.postTransaction);
router.put("/transactions", trans.putTransaction);
router.get("/dashboard", dash.getDashboard);
router.get("/dashboard/:family_id", dash.getDashboard);
std.debug.print("Starting http server listening on port {}\n", .{8081});
// start the server in the current thread, blocking.
@ -102,10 +63,7 @@ fn notFound(_: *httpz.Request, res: *httpz.Response) !void {
// you can set the body directly to a []u8, but note that the memory
// must be valid beyond your handler. Use the res.arena if you need to allocate
// memory for the body.
try res.json(
.{ .success = false, .message = "Not Found" },
.{},
);
res.body = "Not Found";
}
// note that the error handler return `void` and not `!void`
@ -117,15 +75,14 @@ fn errorHandler(req: *httpz.Request, res: *httpz.Response, err: anyerror) void {
pub fn returnError(message: ?[]const u8, comptime statusCode: u16, res: *httpz.Response) void {
comptime {
if (statusCode > 500 or statusCode < 200) {
@compileError("Failed responses must have status codes between 200 and 500");
if (statusCode < 300 or statusCode > 500) {
@compileError("Failed responses must have status codes between 300 and 500");
}
}
res.status = statusCode;
std.log.info("Returning error", .{});
return res.json(.{ .success = false, .message = message }, .{}) catch |err| {
res.json(.{ .success = false, .message = message }, .{}) catch |err| {
std.log.warn("Couldnt create error body: {}", .{err});
res.body = "{ \"success\": false }";
res.body = "{ \"success\": false";
};
}
@ -134,23 +91,3 @@ pub fn returnData(data: anytype, res: *httpz.Response) !void {
res.status = 200;
try res.json(body, .{});
}
const ReqDataError = error{
MalformedBody,
NoData,
};
pub fn getReqJson(req: *httpz.Request, res: *httpz.Response, comptime ReqType: type) ReqDataError!ReqType {
const body_data = req.json(ReqType) catch |err| {
std.debug.print("Malformed body: {any}\nExpected {any} for req {s} on {s}", .{ err, ReqType, @tagName(req.method), req.url.query });
returnError("Bad request: Malformed Body", 400, res);
return ReqDataError.MalformedBody;
};
if (body_data == null) {
std.debug.print("Bad request, no data\nExpected {any} for req {s} on {s}", .{ ReqType, @tagName(req.method), req.url.query });
returnError("Bad request: No Data", 400, res);
return ReqDataError.NoData;
}
var body = body_data.?;
return body;
}

View File

@ -5,8 +5,10 @@ const http = @import("./http_handler.zig");
pub fn main() !void {
std.debug.print("\nStarting Server...\n", .{});
// var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// const allocator = gpa.allocator();
// _ = allocator;
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var db = try Db.init(allocator, null);
defer db.deinit();
// try db.wipeAndMigrateDb();
try http.startHttpServer();
}

View File

@ -1,89 +0,0 @@
const std = @import("std");
const jwt = @import("../.deps/jwt.zig");
const httpz = @import("../.deps/http.zig/src/httpz.zig");
const models = @import("../db/models.zig");
const ztime = @import("../.deps/time.zig");
const handler = @import("../http_handler.zig");
const utils = @import("../utils.zig");
//TODO move these to env variables
const key = "aGVyZUlzQUdpYmVyaXNoS2V5ISE=";
pub const VerifyAuthError = error{
Unauthorized,
NotAuthenticated,
BadToken,
Expired,
};
pub fn verifyRequest(req: *httpz.Request, res: *httpz.Response, user_id: ?u32, family_id: ?u32) !models.Token {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const coded_token = req.headers.get("token");
const now =
@bitCast(u64, std.time.milliTimestamp());
const date = ztime.DateTime.now();
const formatted_now = date.formatAlloc(allocator, "DD.MM.YYYY HH:mm:ss") catch "N/A";
const method = @tagName(req.method);
if (coded_token == null) {
handler.returnError("Unauthorized/NoToken", 401, res);
std.log.info("{s} {s} Unauthorized/NotAuthenticated - @ {s}", .{ method, req.url.query, formatted_now });
return VerifyAuthError.NotAuthenticated;
}
const token = jwt.validate(models.Token, allocator, .HS256, coded_token.?, .{ .key = key }) catch {
handler.returnError("Unauthorized", 400, res);
std.log.info("{s} {s} Unauthorized/BadToken - Token: {s} @ {s}", .{ method, req.url.raw, coded_token.?, formatted_now });
return VerifyAuthError.BadToken;
};
if (user_id != null and user_id.? != token.user_id or family_id != null and family_id.? != token.family_id) {
handler.returnError("Unauthorized", 401, res);
std.log.info("{s} {s} Unauthorized/BadCredentials - User: {} Family: {any} @ {s}, Tried User: {any} Family: {any}", .{ method, req.url.raw, token.user_id, token.family_id, formatted_now, user_id, family_id });
return VerifyAuthError.Unauthorized;
}
if (token.expires_at < now) {
std.log.info("{s} {s} Unauthorized/Expired - User: {} Family: {any} @ {s}", .{ method, req.url.raw, token.user_id, token.family_id, formatted_now });
handler.returnError("Credentials Expired", 403, res);
return VerifyAuthError.Expired;
}
std.log.info("{s} {s} Authorized - User: {} Family: {any} @ {s}", .{ method, req.url.raw, token.user_id, token.family_id, formatted_now });
return token;
}
pub fn generateToken(user: models.User) ![]const u8 {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const now = @bitCast(u64, std.time.milliTimestamp());
var seven_days = ztime.DateTime.now();
seven_days = seven_days.addDays(7);
const token: models.Token = .{ .user_id = user.id, .family_id = user.family_id, .generated_at = now, .expires_at = seven_days.toUnixMilli() };
const encoded_token = try jwt.encode(allocator, .HS256, token, .{ .key = key });
// std.log.info("Generated token for User {} @ {}", .{ user.id, now });
return encoded_token;
}
test {
var gpa = std.testing.allocator_instance;
const allocator = gpa.allocator();
const user = models.User{ .id = 1, .family_id = 1, .budget_id = 1, .name = "Billy", .created_at = 1, .updated_at = 1, .last_activity_at = 1, .hide = 0 };
const user_token = try generateToken(user);
// std.debug.print("user_token: {s}\n", .{user_token});
// var key = try std.testing.allocator.alloc(u8, try base64url.Decoder.calcSizeForSlice(key_base64));
// defer std.testing.allocator.free(key);
// try base64url.Decoder.decode(key, key_base64);
// std.debug.print("key: {s}\n", .{key});
// const user_token: models.Token = .{ .user_id = 1, .family_id = 1, .generated_at = @bitCast(u64, std.time.milliTimestamp()), .expires_at = @bitCast(u64, std.time.milliTimestamp()) };
// const coded_message = try jwt.encode(allocator, .HS256, user_token, .{ .key = key });
const decoded_message = try jwt.validate(models.Token, allocator, .HS256, user_token, .{ .key = key });
std.debug.print("user: {any}\nuser_token: {s}\ndecoded: {any}\n", .{ user, user_token, decoded_message });
}

View File

@ -3,123 +3,122 @@ const httpz = @import("../.deps/http.zig/src/httpz.zig");
const models = @import("../db/models.zig");
const ztime = @import("../.deps/time.zig");
const utils = @import("../utils.zig");
const auth = @import("auth.zig");
const handler = @import("../http_handler.zig");
// pub fn getBudget(req: *httpz.Request, res: *httpz.Response) !void {
// const db = handler.getDb();
pub fn getBudget(req: *httpz.Request, res: *httpz.Response) !void {
const db = handler.getDb();
// var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// const allocator = gpa.allocator();
// const id_str = req.param("id");
// if (id_str == null) {
// res.status = 400;
// res.body = "Bad Request: No Id";
// return;
// }
// const id = std.fmt.parseInt(u32, id_str.?, 0) catch {
// res.status = 401;
// res.body = "Bad Request: Bad Id";
// return;
// };
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const id_str = req.param("id");
if (id_str == null) {
res.status = 400;
res.body = "Bad Request: No Id";
return;
}
const id = std.fmt.parseInt(u32, id_str.?, 0) catch {
res.status = 401;
res.body = "Bad Request: Bad Id";
return;
};
// const budget = try db.selectOneById(models.Budget, allocator, id);
const budget = try db.selectOneById(models.Budget, allocator, id);
// if (budget == null) {
// res.status = 404;
// res.body = "Budget not found";
// return;
// }
if (budget == null) {
res.status = 404;
res.body = "Budget not found";
return;
}
// try res.json(budget.?, .{});
// }
try res.json(budget.?, .{});
}
// const BudgetPostReq = struct {
// id: ?u32,
// family_id: u32,
// name: []const u8,
// created_at: ?u64,
// updated_at: ?u64,
// hide: u8,
// };
const BudgetPostReq = struct {
id: ?u32,
name: []const u8,
created_at: ?u64,
updated_at: ?u64,
hide: u8,
};
// pub fn putBudget(req: *httpz.Request, res: *httpz.Response) !void {
// var db = handler.getDb();
// var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// const allocator = gpa.allocator();
pub fn putBudget(req: *httpz.Request, res: *httpz.Response) !void {
var db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// const body_data = req.json(models.Budget) catch |err| {
// std.debug.print("Malformed body: {any}\n", .{err});
// handler.returnError("Bad Request: Malformed Body", 400, res);
// return;
// };
// if (body_data == null) {
// handler.returnError("Bad Request: No Data", 400, res);
// return;
// }
// var body = body_data.?;
const body_data = req.json(models.Budget) catch |err| {
std.debug.print("Malformed body: {any}\n", .{err});
handler.returnError("Bad Request: Malformed Body", 400, res);
return;
};
if (body_data == null) {
handler.returnError("Bad Request: No Data", 400, res);
return;
}
var body = body_data.?;
// // Add Budget
// const now = @intCast(u64, std.time.milliTimestamp());
// // Update existing Budget
// body.updated_at = now;
// try db.updateById(models.Budget, body);
// Add Budget
const now = @intCast(u64, std.time.milliTimestamp());
// Update existing Budget
body.updated_at = now;
try db.updateById(models.Budget, body);
// const query = models.createSelectOnIdQuery(models.Transaction);
// const updated_budget = try db.selectOne(models.Budget, allocator, query, .{ .id = body.id });
// if (updated_budget) |budget| {
// try handler.returnData(budget, res);
// } else {
// handler.returnError("Internal Server Error", 500, res);
// }
// return;
// }
const query = models.createSelectOnIdQuery(models.Transaction);
const updated_budget = try db.selectOne(models.Budget, allocator, query, .{ .id = body.id });
if (updated_budget) |budget| {
try handler.returnData(budget, res);
} else {
handler.returnError("Internal Server Error", 500, res);
}
return;
}
// pub fn postBudget(req: *httpz.Request, res: *httpz.Response) !void {
// comptime {
// const putReqLen = @typeInfo(BudgetPostReq).Struct.fields.len;
// const budgetLen = @typeInfo(models.Budget).Struct.fields.len;
// if (putReqLen != budgetLen) {
// @compileError(std.fmt.comptimePrint("BudgetPostReq does not equal Budget model struct, fields inconsistent", .{}));
// }
// }
pub fn postBudget(req: *httpz.Request, res: *httpz.Response) !void {
comptime {
const putReqLen = @typeInfo(BudgetPostReq).Struct.fields.len;
const budgetLen = @typeInfo(models.Budget).Struct.fields.len;
if (putReqLen != budgetLen) {
@compileError(std.fmt.comptimePrint("BudgetPutReq does not equal Budget model struct, fields inconsistent", .{}));
}
}
// var db = handler.getDb();
// var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// const allocator = gpa.allocator();
var db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// const body_data = req.json(BudgetPostReq) catch |err| {
// std.debug.print("Malformed body: {any}\n", .{err});
// handler.returnError("Bad request: Malformed Body", 400, res);
// return;
// };
// if (body_data == null) {
// handler.returnError("Bad request: No Data", 400, res);
// return;
// }
// var body = body_data.?;
const body_data = req.json(BudgetPostReq) catch |err| {
std.debug.print("Malformed body: {any}\n", .{err});
handler.returnError("Bad request: Malformed Body", 400, res);
return;
};
if (body_data == null) {
handler.returnError("Bad request: No Data", 400, res);
return;
}
var body = body_data.?;
// if (body.id != null) {
// handler.returnError("Bad Request: ID", 400, res);
// }
// // Add Budget
// const now = @intCast(u64, std.time.milliTimestamp());
// // Create Budget
// body.created_at = now;
// body.updated_at = now;
if (body.id != null) {
handler.returnError("Bad Request: ID", 400, res);
}
// Add Budget
const now = @intCast(u64, std.time.milliTimestamp());
// Create Budget
body.created_at = now;
body.updated_at = now;
// try db.insert(models.Budget, utils.removeStructFields(body, &[_]u8{0}));
try db.insert(models.Budget, utils.removeStructFields(body, &[_]u8{0}));
// // Get Budget
// const query = try models.createSelectOnFieldQuery(models.Budget, null, "created_at", "=");
// const updated_budget = try db.selectOne(models.Budget, allocator, query, .{ .created_at = body.created_at });
// if (updated_budget) |budget| {
// try handler.returnData(budget, res);
// } else {
// handler.returnError("Internal Server Error", 500, res);
// }
// return;
// }
// Get Budget
const query = try models.createSelectOnFieldQuery(models.Budget, null, "created_at", "=");
const updated_budget = try db.selectOne(models.Budget, allocator, query, .{ .created_at = body.created_at });
if (updated_budget) |budget| {
try handler.returnData(budget, res);
} else {
handler.returnError("Internal Server Error", 500, res);
}
return;
}
const BudgetCatPostReq = struct {
id: ?u32,
@ -161,16 +160,6 @@ pub fn postBudgetCategory(req: *httpz.Request, res: *httpz.Response) !void {
handler.returnError("Bad request: ID", 400, res);
return;
}
const budget = try db.selectOneById(models.Budget, allocator, body.budget_id);
if (budget == null) {
handler.returnError("No budget found", 404, res);
}
_ = auth.verifyRequest(req, res, null, budget.?.family_id) catch {
return;
};
// Add Budget
const now = @intCast(u64, std.time.milliTimestamp());
// Create Budget
@ -181,14 +170,14 @@ pub fn postBudgetCategory(req: *httpz.Request, res: *httpz.Response) !void {
// Get Budget
const query = try models.createSelectOnFieldQuery(models.BudgetCategory, null, "created_at", "=");
const updated_budget_cat = try db.selectOne(models.BudgetCategory, allocator, query, .{ .created_at = body.created_at });
if (updated_budget_cat == null) {
const updated_budget = try db.selectOne(models.BudgetCategory, allocator, query, .{ .created_at = body.created_at });
if (updated_budget) |budget| {
try handler.returnData(budget, res);
} else {
std.debug.print("Could not find inserted budget", .{});
handler.returnError("Internal Server Error", 500, res);
return;
}
try handler.returnData(updated_budget_cat.?, res);
return;
}
pub fn putBudgetCategory(req: *httpz.Request, res: *httpz.Response) !void {
@ -207,15 +196,6 @@ pub fn putBudgetCategory(req: *httpz.Request, res: *httpz.Response) !void {
}
var budget_category = body_data.?;
const budget = try db.selectOneById(models.Budget, allocator, budget_category.budget_id);
if (budget == null) {
handler.returnError("No budget found", 404, res);
}
_ = auth.verifyRequest(req, res, null, budget.?.family_id) catch {
return;
};
const now = @intCast(u64, std.time.milliTimestamp());
// Update existing Budget
@ -223,47 +203,12 @@ pub fn putBudgetCategory(req: *httpz.Request, res: *httpz.Response) !void {
try db.updateById(models.BudgetCategory, budget_category);
const query = models.createSelectOnIdQuery(models.BudgetCategory);
const updated_budget_cat = try db.selectOne(models.BudgetCategory, allocator, query, .{ .id = budget_category.id });
if (updated_budget_cat == null) {
const updated_budget = try db.selectOne(models.BudgetCategory, allocator, query, .{ .id = budget_category.id });
if (updated_budget) |budget| {
try handler.returnData(budget, res);
} else {
std.debug.print("Could not find inserted budget", .{});
handler.returnError("Internal Server Error", 500, res);
return;
}
try handler.returnData(updated_budget_cat.?, res);
return;
}
const deleteIdReq = struct {
id: u32,
};
pub fn deleteBudgetCategory(req: *httpz.Request, res: *httpz.Response) !void {
var db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const body = handler.getReqJson(req, res, deleteIdReq) catch {
return;
};
const budget_category = try db.selectOneById(models.BudgetCategory, allocator, body.id);
if (budget_category == null) {
return handler.returnError("Cannot find budget", 404, res);
}
const budget = try db.selectOneById(models.Budget, allocator, budget_category.?.budget_id);
if (budget == null) {
return handler.returnError("Cannot find budget", 404, res);
}
_ = auth.verifyRequest(req, res, null, budget.?.family_id) catch {
return;
};
try db.updateHideById(models.BudgetCategory, true, body.id);
const updated_budget_category = try db.selectOneById(models.BudgetCategory, allocator, body.id);
if (budget_category == null) {
return handler.returnError("Could not delete category", 500, res);
}
return try handler.returnData(updated_budget_category.?, res);
}

View File

@ -4,9 +4,7 @@ const models = @import("../db/models.zig");
const ztime = @import("../.deps/time.zig");
const utils = @import("../utils.zig");
const trans = @import("transactions.zig");
const note = @import("shared_note.zig");
const handler = @import("../http_handler.zig");
const auth = @import("auth.zig");
pub fn getDashboard(req: *httpz.Request, res: *httpz.Response) !void {
const db = handler.getDb();
@ -14,62 +12,41 @@ pub fn getDashboard(req: *httpz.Request, res: *httpz.Response) !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const token = auth.verifyRequest(req, res, null, null) catch {
const family_id_str = req.param("family_id");
if (family_id_str == null) {
res.status = 400;
res.body = "Bad Request: No FamilyId";
return;
}
const family_id = std.fmt.parseInt(u32, family_id_str.?, 0) catch {
res.status = 400;
res.body = "Bad Request: Bad FamilyId";
return;
};
// const family_id = std.fmt.parseInt(u32, family_id_str.?, 0) catch {
// res.status = 400;
// res.body = "Bad Request: Bad FamilyId";
// return;
// };
const family = db.selectOneById(models.Family, allocator, token.family_id) catch |err| {
std.log.err("Error getting family in dashboard: {any}", .{err});
return handler.returnError("Unexpected Server Error", 500, res);
const family = db.selectOneById(models.Family, allocator, family_id) catch |err| {
if (err == error.SQLiteError) {
res.status = 404;
res.body = "Family Not Found";
return;
}
std.debug.print("Error while getting family: {}\n", .{err});
res.status = 500;
res.body = "Internal Server Error";
return;
};
if (family == null) {
std.log.err("Family not found, invalidating client token", .{});
return handler.returnError("Family does not exist or forbidden", 403, res);
res.status = 404;
res.body = "Family Not Found";
return;
}
const user = db.selectOneById(models.User, allocator, token.user_id) catch |err| {
std.log.err("User not found, invalidating client token, Err: {any}", .{err});
return handler.returnError("Could not get user or forbidden", 403, res);
};
if (user == null) {
std.log.err("User not found, invalidating client token", .{});
return handler.returnError("User not found or forbidden", 403, res);
}
const transactions = trans.fetchTransFromDb(allocator, family.?.id) catch |err| {
std.log.err("Unexpected error while getting transactions: {any}", .{err});
handler.returnError("Internal Server Error", 500, res);
return;
};
const notes = note.fetchNotesFromDb(allocator, family.?.id) catch |err| {
std.log.err("Unexpected error while getting transactions: {any}", .{err});
handler.returnError("Internal Server Error", 500, res);
return;
};
const budget_query = try models.createSelectOnFieldQuery(models.Budget, null, "family_id", "=");
const budget = db.selectOne(models.Budget, allocator, budget_query, .{ .family_id = token.family_id }) catch |err| {
std.log.err("Unexpected error while getting budget: {any}", .{err});
handler.returnError("Internal Server Error", 500, res);
return;
};
const transactions = try trans.fetchTransFromDb(allocator, family.?.budget_id);
const budget = try db.selectOneById(models.Budget, allocator, family.?.budget_id);
var budget_categories: ?[]models.BudgetCategory = null;
if (budget != null) {
budget_categories = db.selectAllWhere(models.BudgetCategory, allocator, "WHERE budget_id = ? AND hide = ?", .{ .budget_id = budget.?.id, .hide = 0 }, null, null, null, null) catch |err| {
std.log.err("Unexpected error while getting budget categories: {any}", .{err});
handler.returnError("Internal Server Error", 500, res);
return;
};
budget_categories = try db.selectAllWhere(models.BudgetCategory, allocator, "WHERE budget_id = ? AND hide = ?", .{ .budget_id = budget.?.id, .hide = 0 }, null);
}
if (budget_categories == null) {
@ -77,12 +54,9 @@ pub fn getDashboard(req: *httpz.Request, res: *httpz.Response) !void {
}
const response_body = .{
.family = family.?,
.user = user.?,
.budget = budget,
.budget_categories = budget_categories,
.transactions = transactions,
.shared_notes = notes,
.success = true,
};
try res.json(response_body, .{});
}

View File

@ -1,155 +0,0 @@
const std = @import("std");
const httpz = @import("../.deps/http.zig/src/httpz.zig");
const models = @import("../db/models.zig");
// const ztime = @import("../.deps/time.zig");
const utils = @import("../utils.zig");
const auth = @import("auth.zig");
const handler = @import("../http_handler.zig");
pub fn fetchNotesFromDb(allocator: std.mem.Allocator, family_id: u32) !?[]models.SharedNote {
var db = handler.getDb();
const notes = db.selectAllWhere(
models.SharedNote,
allocator,
"WHERE family_id = ? and hide = ?",
.{ .family_id = family_id, .hide = 0 },
"updated_at",
"DESC",
true,
10,
) catch |err| {
std.debug.print("Error while getting shared notes: {any}", .{err});
return err;
};
return notes;
}
pub fn getSharedNotes(req: *httpz.Request, res: *httpz.Response) !void {
var db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var str_limit: ?[]const u8 = req.param("limit");
if (str_limit == null) {
str_limit = "10";
}
const limit = std.fmt.parseInt(u32, str_limit.?, 0) catch {
handler.returnError("Bad Request: Bad Limit", 401, res);
return;
};
const token = auth.verifyRequest(req, res, null, null) catch {
return;
};
const notes = db.selectAllWhere(
models.SharedNote,
allocator,
"WHERE family_id = ? and hide = ?",
.{ .family_id = token.family_id, .hide = 0 },
"updated_at",
"DESC",
true,
limit,
) catch |err| {
std.debug.print("Error while getting shared notes: {any}", .{err});
return err;
};
try res.json(.{ .notes = notes }, .{});
return;
}
pub fn putSharedNote(req: *httpz.Request, res: *httpz.Response) !void {
var db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const body_data = req.json(models.SharedNote) catch |err| {
std.debug.print("Malformed body: {any}\n", .{err});
handler.returnError("Bad request: Malformed Body", 400, res);
return;
};
if (body_data == null) {
handler.returnError("Bad request: No Data", 400, res);
return;
}
var shared_note = body_data.?;
_ = auth.verifyRequest(req, res, shared_note.created_by_user_id, shared_note.family_id) catch {
return;
};
const now = @intCast(u64, std.time.milliTimestamp());
shared_note.updated_at = now;
try db.updateById(models.SharedNote, shared_note);
const updated_note = try db.selectOneById(models.SharedNote, allocator, shared_note.id);
if (updated_note) |note| {
try handler.returnData(note, res);
} else {
handler.returnError("Internal Server Error", 500, res);
}
return;
}
const NotePostReq = struct {
id: ?u32,
family_id: u32,
created_by_user_id: u32,
content: []const u8,
title: []const u8,
color: ?[]const u8,
tag_ids: []const u8,
is_markdown: u2,
created_at: ?u64,
updated_at: ?u64,
hide: u8,
};
pub fn postSharedNote(req: *httpz.Request, res: *httpz.Response) !void {
comptime {
const postReqLen = @typeInfo(NotePostReq).Struct.fields.len;
const noteLen = @typeInfo(models.SharedNote).Struct.fields.len;
if (postReqLen != noteLen) {
@compileError(std.fmt.comptimePrint("SharedNotePutReq does not equal SharedNote model struct, fields inconsistent", .{}));
}
}
var db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const body_data = req.json(NotePostReq) catch |err| {
std.debug.print("Malformed body: {any}\n", .{err});
handler.returnError("Bad request: Malformed Body", 400, res);
return;
};
if (body_data == null) {
handler.returnError("Bad request: No Data", 400, res);
return;
}
var body = body_data.?;
_ = auth.verifyRequest(req, res, body.created_by_user_id, body.family_id) catch {
return;
};
const now = @intCast(u64, std.time.milliTimestamp());
body.created_at = now;
body.updated_at = now;
// remove the null id field for insertion
try db.insert(models.SharedNote, utils.removeStructFields(body, &[_]u8{0}));
// Get new SharedNote
const query = try models.createSelectOnFieldQuery(models.SharedNote, null, "created_at", "=");
const updated_note = try db.selectOne(models.SharedNote, allocator, query, .{ .created_at = body.created_at });
if (updated_note) |note| {
try handler.returnData(note, res);
} else {
handler.returnError("Internal Server Error", 500, res);
}
return;
}

View File

@ -3,84 +3,56 @@ const httpz = @import("../.deps/http.zig/src/httpz.zig");
const models = @import("../db/models.zig");
const ztime = @import("../.deps/time.zig");
const utils = @import("../utils.zig");
const time = @import("../.deps/datetime.zig");
const tz = @import("../.deps/timezones.zig");
const auth = @import("auth.zig");
const handler = @import("../http_handler.zig");
pub fn fetchTransFromDb(allocator: std.mem.Allocator, family_id: u32) !?[]models.Transaction {
pub fn fetchTransFromDb(allocator: std.mem.Allocator, budget_id: u32) !?[]models.Transaction {
var db = handler.getDb();
const now = time.Datetime.now();
var beginning_of_month = try time.Datetime.fromDate(now.date.year, now.date.month, 1);
// const timezone_begin = beginning_of_month.shiftTimezone(&tz.US.Mountain);
const now = ztime.DateTime.now();
const beginningOfMonth = ztime.DateTime.init(now.years, now.months, 0, 0, 0, 0);
const timezone_begin = beginning_of_month.shiftHours(6);
// std.log.info("Beginning: {} Shifted: {}", .{ beginning_of_month.date.toTimestamp(), timezone_begin.date.toTimestamp() });
// const unix = @bitCast(u64, beginning_of_month.date.toTimestamp() + beginning_of_month.time.toTimestamp());
const begin_time = @bitCast(u64, timezone_begin.date.toTimestamp() + timezone_begin.time.toTimestamp());
// std.log.info("Fetching transactions after beginning of month: unix {}, adjusted {}", .{ unix, begin_time });
comptime {
if (!std.mem.eql(u8, @typeInfo(models.Transaction).Struct.fields[7].name, "date")) {
return error{TransactionModelError};
}
}
const budget_query = models.createSelectOnFieldQuery(models.Budget, null, "family_id", "=") catch |err| {
std.debug.print("Error while creating budget query: {any}", .{err});
return err;
};
const budget = db.selectOne(models.Budget, allocator, budget_query, .{ .family_id = family_id }) catch |err| {
std.debug.print("Error while getting budget: {any}", .{err});
return err;
};
if (budget == null) {
return null;
}
const transactions = db.selectAllWhere(
const transactions = try db.selectAllWhere(
models.Transaction,
allocator,
"WHERE budget_id = ? AND date > ? AND hide = ?",
.{ .budget_id = budget.?.id, .date = begin_time, .hide = 0 },
.{ .budget_id = budget_id, .date = beginningOfMonth.toUnixMilli(), .hide = 0 },
null,
null,
null,
null,
) catch |err| {
std.debug.print("Error while getting transactions query: {any}", .{err});
return err;
};
);
return transactions;
}
// pub fn getTransactions(req: *httpz.Request, res: *httpz.Response) !void {
// auth.verifyRequest();
// var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// const allocator = gpa.allocator();
pub fn getTransactions(req: *httpz.Request, res: *httpz.Response) !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// const budget_id_str = req.param("budget_id");
// if (budget_id_str == null) {
// handler.returnError("Bad request", 400, res);
// return;
// }
// const budget_id = std.fmt.parseInt(u32, budget_id_str.?, 0) catch {
// handler.returnError("Bad request", 400, res);
// return;
// };
const budget_id_str = req.param("budget_id");
if (budget_id_str == null) {
handler.returnError("Bad request", 400, res);
return;
}
const budget_id = std.fmt.parseInt(u32, budget_id_str.?, 0) catch {
handler.returnError("Bad request", 400, res);
return;
};
// const transactions = try fetchTransFromDb(allocator, budget_id);
const transactions = try fetchTransFromDb(allocator, budget_id);
// if (transactions == null) {
// res.status = 200;
// res.body = "";
// return;
// }
// // std.debug.print("Transactions got:\n", .{});
// // for (transactions.?) |transaction| {
// // std.debug.print("\t{any}\n", .{transaction});
// // }
// try handler.returnData(.{ .transactions = transactions.? }, res);
// }
if (transactions == null) {
res.status = 200;
res.body = "";
return;
}
// std.debug.print("Transactions got:\n", .{});
// for (transactions.?) |transaction| {
// std.debug.print("\t{any}\n", .{transaction});
// }
try handler.returnData(.{ .transactions = transactions.? }, res);
}
pub fn putTransaction(req: *httpz.Request, res: *httpz.Response) !void {
var db = handler.getDb();
@ -98,16 +70,9 @@ pub fn putTransaction(req: *httpz.Request, res: *httpz.Response) !void {
}
var transaction = body_data.?;
const budget = try db.selectOneById(models.Budget, allocator, transaction.budget_id);
if (budget == null) {
return handler.returnError("Budget for transaction not found or forbidden", 403, res);
}
_ = auth.verifyRequest(req, res, transaction.created_by_user_id, budget.?.family_id) catch {
return;
};
// Add Transaction
const now = @intCast(u64, std.time.milliTimestamp());
// Update existing Transaction
transaction.updated_at = now;
try db.updateById(models.Transaction, transaction);
@ -127,7 +92,7 @@ const TransPostReq = struct {
type: []const u8,
memo: ?[]const u8,
budget_id: u32,
created_by_user_id: u32,
added_by_user_id: u32,
budget_category_id: ?u32,
date: u64,
created_at: ?u64,
@ -137,9 +102,9 @@ const TransPostReq = struct {
pub fn postTransaction(req: *httpz.Request, res: *httpz.Response) !void {
comptime {
const postReqLen = @typeInfo(TransPostReq).Struct.fields.len;
const putReqLen = @typeInfo(TransPostReq).Struct.fields.len;
const transLen = @typeInfo(models.Transaction).Struct.fields.len;
if (postReqLen != transLen) {
if (putReqLen != transLen) {
@compileError(std.fmt.comptimePrint("TransactionPutReq does not equal Transaction model struct, fields inconsistent", .{}));
}
}
@ -147,23 +112,26 @@ pub fn postTransaction(req: *httpz.Request, res: *httpz.Response) !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var body = handler.getReqJson(req, res, TransPostReq) catch {
const body_data = req.json(TransPostReq) catch |err| {
std.debug.print("Malformed body: {any}\n", .{err});
handler.returnError("Bad request: Malformed Body", 400, res);
return;
};
const budget = try db.selectOneById(models.Budget, allocator, body.budget_id);
if (budget == null) {
return handler.returnError("Budget for transaction not foud or forbidden", 403, res);
if (body_data == null) {
handler.returnError("Bad request: No Data", 400, res);
return;
}
_ = auth.verifyRequest(req, res, body.created_by_user_id, budget.?.family_id) catch {
var body = body_data.?;
if (body.id != null) {
handler.returnError("Bad request: ID", 400, res);
return;
};
}
const now = @intCast(u64, std.time.milliTimestamp());
body.created_at = now;
body.updated_at = now;
// remove the null id field for insertion
try db.insert(models.Transaction, utils.removeStructFields(body, &[_]u8{0}));
// Get new Transaction
@ -176,38 +144,3 @@ pub fn postTransaction(req: *httpz.Request, res: *httpz.Response) !void {
}
return;
}
const deleteIdReq = struct {
id: u32,
};
pub fn deleteTransaction(req: *httpz.Request, res: *httpz.Response) !void {
var db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const body = handler.getReqJson(req, res, deleteIdReq) catch {
return;
};
const transaction = try db.selectOneById(models.Transaction, allocator, body.id);
if (transaction == null) {
return handler.returnError("Id invalid, not a transaction", 400, res);
}
const budget = try db.selectOneById(models.Budget, allocator, transaction.?.budget_id);
if (budget == null) {
return handler.returnError("Cannot find associated budget", 404, res);
}
_ = auth.verifyRequest(req, res, null, budget.?.family_id) catch {
return;
};
try db.updateHideById(models.Transaction, true, body.id);
const updated_transaction = try db.selectOneById(models.Transaction, allocator, body.id);
if (updated_transaction == null) {
return handler.returnError("Could not delete transaction", 500, res);
}
return try handler.returnData(updated_transaction.?, res);
}

View File

@ -2,208 +2,8 @@ const std = @import("std");
const httpz = @import("../.deps/http.zig/src/httpz.zig");
const models = @import("../db/models.zig");
const utils = @import("../utils.zig");
const auth = @import("auth.zig");
const handler = @import("../http_handler.zig");
const ztime = @import("../.deps/time.zig");
const LoginReq = struct {
username: ?[]const u8,
email: ?[]const u8,
password: []const u8,
};
// POST endpoint for user login requests
pub fn login(req: *httpz.Request, res: *httpz.Response) !void {
const db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const body_data = req.json(LoginReq) catch |err| {
std.debug.print("Malformed body: {any}\n", .{err});
handler.returnError("Bad Request: Malformed Body", 400, res);
return;
};
if (body_data == null) {
handler.returnError("Bad Request: No Data", 400, res);
return;
}
const formatted_now = ztime.DateTime.now().formatAlloc(allocator, "DD.MM.YYYY HH:mm:ss") catch "N/A";
std.log.info("{s} {s} @ {s}\n", .{ @tagName(req.method), req.url.raw, formatted_now });
var body = body_data.?;
if (body.username == null and body.email == null) {
handler.returnError("Bad Request: Missing username / email", 400, res);
return;
} else if (body.username != null and body.email != null) {
handler.returnError("Bad Request: Provided Username and Email", 400, res);
return;
}
var user: ?models.User = null;
const password_hash = try utils.hashPassword(allocator, body.password);
if (body.username != null) {
const query =
"WHERE pass_hash = ? and username = ?;";
user = try db.selectOne(models.User, allocator, try models.createRawSelectQuery(
models.User,
query,
), .{ .pass_hash = password_hash, .username = body.username.? });
} else {
const query =
"WHERE pass_hash = ? and email = ?;";
user = try db.selectOne(models.User, allocator, try models.createRawSelectQuery(
models.User,
query,
), .{ .pass_hash = password_hash, .email = body.email.? });
}
if (user == null) {
handler.returnError("User not found or incorrect password", 200, res);
return;
} else if (user.?.hide == 1) {
handler.returnError("Account has been closed", 200, res);
return;
}
const token = try auth.generateToken(user.?);
try handler.returnData(.{ .token = token }, res);
}
const SignupReq = struct {
name: []const u8,
username: []const u8,
email: ?[]const u8,
password: []const u8,
family_code: ?[]const u8,
budget_name: []const u8,
};
// POST endpoint for user signups
pub fn signup(req: *httpz.Request, res: *httpz.Response) !void {
const db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const body_data = req.json(SignupReq) catch |err| {
std.debug.print("Malformed body: {any}\n", .{err});
handler.returnError("Bad Request: Malformed Body", 400, res);
return;
};
if (body_data == null) {
handler.returnError("Bad Request: No Data", 400, res);
return;
}
const formatted_now = ztime.DateTime.now().formatAlloc(allocator, "DD.MM.YYYY HH:mm:ss") catch "N/A";
std.log.info("{s} {s} @ {s}\n", .{ @tagName(req.method), req.url.raw, formatted_now });
var body = body_data.?;
// if (body.username == null and body.email == null) {
// handler.returnError("Bad Request: Missing username / email", 400, res);
// return;
// }
const password_hash = try utils.hashPassword(allocator, body.password);
const now = @bitCast(u64, std.time.milliTimestamp());
const uname_query =
"WHERE username = ?;";
var temp_user = try db.selectOne(models.User, allocator, try models.createRawSelectQuery(
models.User,
uname_query,
), .{ .username = body.username });
if (temp_user != null) {
handler.returnError("Username is unavailable", 200, res);
return;
}
if (body.email != null) {
const email_query =
"WHERE email = ?;";
temp_user = try db.selectOne(models.User, allocator, try models.createRawSelectQuery(
models.User,
email_query,
), .{ .email = body.email.? });
if (temp_user != null) {
handler.returnError("Email is unavailable", 200, res);
return;
}
}
var family: ?models.Family = null;
var budget: ?models.Budget = null;
var user: ?models.User = null;
if (body.family_code == null) {
const new_family = .{
.code = utils.generateRandomString(allocator) catch null,
.created_at = now,
.updated_at = now,
.hide = 0,
};
try db.insert(models.Family, new_family);
const family_query = try models.createSelectOnFieldQuery(models.Family, null, "code", "=");
family = try db.selectOne(models.Family, allocator, family_query, .{ .code = new_family.code });
if (family == null) {
handler.returnError("Unable to create family", 500, res);
return;
}
const new_budget = .{
.family_id = family.?.id,
.name = body.budget_name,
.created_at = now,
.updated_at = now,
.hide = 0,
.expected_income = null,
};
try db.insert(models.Budget, new_budget);
const budget_query = try models.createSelectOnFieldQuery(models.Budget, null, "created_at", "=");
budget = try db.selectOne(models.Budget, allocator, budget_query, .{ .created_at = now });
if (budget == null) {
handler.returnError("Unable to create budget", 500, res);
return;
}
} else {
const family_query = try models.createSelectOnFieldQuery(models.Family, null, "code", "=");
family = try db.selectOne(models.Family, allocator, family_query, .{ .code = body.family_code.? });
if (family == null) {
return handler.returnError("Invalid Family Code", 404, res);
}
const budget_query = try models.createSelectOnFieldQuery(models.Budget, null, "family_id", "=");
budget = try db.selectOne(models.Budget, allocator, budget_query, .{ .family_id = family.?.id });
if (budget == null) {
return handler.returnError("Could not find Family Budget", 404, res);
}
}
const new_user = .{ .name = body.name, .username = body.username, .email = body.email, .pass_hash = password_hash, .family_id = family.?.id, .budget_id = budget.?.id, .created_at = now, .updated_at = now, .last_activity_at = now, .hide = 0 };
try db.insert(models.User, new_user);
const query = try models.createSelectOnFieldQuery(models.User, null, "pass_hash", "=");
user = try db.selectOne(models.User, allocator, query, .{ .pass_hash = password_hash });
if (user == null) {
handler.returnError("Unable to create new user", 500, res);
return;
}
// std.log.info("User created: {any}\nFamily created: {any}\n", .{ user.?, family.? });
const token = try auth.generateToken(user.?);
try handler.returnData(.{ .user = user, .token = token }, res);
}
pub fn getUser(req: *httpz.Request, res: *httpz.Response) !void {
const db = handler.getDb();
@ -223,10 +23,7 @@ pub fn getUser(req: *httpz.Request, res: *httpz.Response) !void {
const user = try db.selectOneById(models.User, allocator, id);
if (user == null) {
_ = auth.verifyRequest(req, res, id, user.family_id) catch {
return;
};
handler.returnError("Error: User Not Found of Forbidden", 403, res);
handler.returnError("Error: User Not Found", 404, res);
return;
}
@ -234,6 +31,7 @@ pub fn getUser(req: *httpz.Request, res: *httpz.Response) !void {
}
const UserPostReq = struct {
id: ?u32,
name: []const u8,
family_id: u32,
budget_id: u32,
@ -259,10 +57,6 @@ pub fn putUser(req: *httpz.Request, res: *httpz.Response) !void {
}
var body = body_data.?;
_ = auth.verifyRequest(req, res, body.id, body.family_id) catch {
return;
};
// Add User
const now = @intCast(u64, std.time.milliTimestamp());
// Update existing User
@ -282,74 +76,74 @@ pub fn putUser(req: *httpz.Request, res: *httpz.Response) !void {
// try res.json(user, .{});
}
// pub fn postUser(req: *httpz.Request, res: *httpz.Response) !void {
// comptime {
// const putReqLen = @typeInfo(UserPostReq).Struct.fields.len;
// const userLen = @typeInfo(models.User).Struct.fields.len;
// if (putReqLen != userLen) {
// @compileError(std.fmt.comptimePrint("UserPutReq does not equal User model struct, fields inconsistent", .{}));
// }
// }
pub fn postUser(req: *httpz.Request, res: *httpz.Response) !void {
comptime {
const putReqLen = @typeInfo(UserPostReq).Struct.fields.len;
const userLen = @typeInfo(models.User).Struct.fields.len;
if (putReqLen != userLen) {
@compileError(std.fmt.comptimePrint("UserPutReq does not equal User model struct, fields inconsistent", .{}));
}
}
// const db = handler.getDb();
// var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// const allocator = gpa.allocator();
const db = handler.getDb();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// const body_data = req.json(UserPostReq) catch |err| {
// std.debug.print("Malformed body: {any}\n", .{err});
// handler.returnError("Bad request: Malformed Body", 400, res);
// return;
// };
// if (body_data == null) {
// handler.returnError("Bad request: No Data", 400, res);
// return;
// }
// var body = body_data.?;
const body_data = req.json(UserPostReq) catch |err| {
std.debug.print("Malformed body: {any}\n", .{err});
handler.returnError("Bad request: Malformed Body", 400, res);
return;
};
if (body_data == null) {
handler.returnError("Bad request: No Data", 400, res);
return;
}
var body = body_data.?;
// if (body.id != null) {
// handler.returnError("Bad request: ID", 400, res);
// return;
// }
// // Add User
// const now = @intCast(u64, std.time.milliTimestamp());
// // Create User
// body.created_at = now;
// body.last_activity_at = now;
// body.updated_at = now;
if (body.id != null) {
handler.returnError("Bad request: ID", 400, res);
return;
}
// Add User
const now = @intCast(u64, std.time.milliTimestamp());
// Create User
body.created_at = now;
body.last_activity_at = now;
body.updated_at = now;
// try db.insert(models.User, utils.removeStructFields(body, &[_]u8{0}));
try db.insert(models.User, utils.removeStructFields(body, &[_]u8{0}));
// // Get new User
// const query = try models.createSelectOnFieldQuery(models.User, null, "created_at", "=");
// const updated_user = try db.selectOne(models.User, allocator, query, .{ .created_at = body.created_at });
// if (updated_user) |user| {
// try handler.returnData(user, res);
// } else {
// handler.returnError("Internal Server Error", 500, res);
// }
// return;
// }
// Get new User
const query = try models.createSelectOnFieldQuery(models.User, null, "created_at", "=");
const updated_user = try db.selectOne(models.User, allocator, query, .{ .created_at = body.created_at });
if (updated_user) |user| {
try handler.returnData(user, res);
} else {
handler.returnError("Internal Server Error", 500, res);
}
return;
}
// pub fn deleteUser(req: *httpz.Request, res: *httpz.Response) !void {
// const db = handler.getDb();
pub fn deleteUser(req: *httpz.Request, res: *httpz.Response) !void {
const db = handler.getDb();
// const user_id = req.param("id");
// if (res.body) |_| {
// handler.returnError("Bad Request", 400, res);
// return;
// }
// if (user_id) |id_str| {
// const id = std.fmt.parseInt(u32, id_str, 0) catch {
// handler.returnError("Bad Request: Invalid Id", 400, res);
// return;
// };
// db.deleteById(models.User, id) catch |err| {
// std.debug.print("Error while deleting user: {}\n", .{err});
// handler.returnError("Internal Server Error", 500, res);
// return;
// };
// } else {
// handler.returnError("Bad Request: Missing ID", 400, res);
// }
// return;
// }
const user_id = req.param("id");
if (res.body) |_| {
handler.returnError("Bad Request", 400, res);
return;
}
if (user_id) |id_str| {
const id = std.fmt.parseInt(u32, id_str, 0) catch {
handler.returnError("Bad Request: Invalid Id", 400, res);
return;
};
db.deleteById(models.User, id) catch |err| {
std.debug.print("Error while deleting user: {}\n", .{err});
handler.returnError("Internal Server Error", 500, res);
return;
};
} else {
handler.returnError("Bad Request: Missing ID", 400, res);
}
return;
}

View File

@ -1,20 +1,11 @@
const std = @import("std");
const HASH_SEED: u64 = 6065983110;
const HASH_SALT: []const u8 = "ZnNLSRbY12DpPeMaPooKhOsxk7Qq325a2KF8EoIIeOaEz";
fn SpreadResult(comptime Base: type, comptime Additional: type) type {
comptime {
// const type_info = @typeInfo(Base);
// if (@Type(type_info) != std.builtin.Type.Struct) {
// @compileError("Cannot have anything but struct but got: " ++ @typeName(Base));
// }
if (@typeInfo(Base) != .Struct) {
@compileError("Provided non struct to struct concat: " ++ @typeName(Base));
}
if (@typeInfo(Additional) != .Struct) {
@compileError("Provided non struct to struct concat: " ++ @typeName(Additional));
}
// _ = std.fmt.comptimePrint("Passed in base: {} {}", .{ Base, type_info });
}
var fields = @typeInfo(Base).Struct.fields;
@ -39,11 +30,6 @@ pub fn structConcatFields(
base: anytype,
additional: anytype,
) SpreadResult(@TypeOf(base), @TypeOf(additional)) {
// comptime {
// if (@typeInfo(@TypeOf(base)) != .Struct or @typeInfo(@TypeOf(additional)) != .Struct) {
// @compileError("Provided non struct to struct concat");
// }
// }
const Base = @TypeOf(base);
const Additional = @TypeOf(additional);
var result: SpreadResult(Base, Additional) = undefined;
@ -109,44 +95,12 @@ pub fn removeStructFields(
return result;
}
pub fn generateRandomString(allocator: std.mem.Allocator) ![]const u8 {
const chars: []const u8 = "ABCDEFGHIJKJMNOPQRSTUVWXYZ1234567890";
var xoshiro = std.rand.DefaultPrng.init(@bitCast(u64, std.time.milliTimestamp()));
const rng = xoshiro.random();
var code: []u8 = try allocator.alloc(u8, 5);
for (0..code.len) |i| {
const char_index = rng.uintLessThan(u8, chars.len + 1) % chars.len;
code[i] = chars[char_index];
}
std.log.info("Generated family code: {s}", .{code});
return code;
}
pub fn hashPassword(allocator: std.mem.Allocator, password: []const u8) !u32 {
const salted_password = try std.mem.concat(allocator, u8, &[_][]const u8{ password, HASH_SALT });
const password_hash = @truncate(u32, std.hash.Wyhash.hash(HASH_SEED, salted_password));
return password_hash;
}
test {
// const vote = .{ .id = 0, .createdAt = "DATE" };
// const data = structConcatFields(vote, .{ .id2 = vote.id });
// std.log.err("\n{any}\n", .{data});
// const user = .{ .id = 0, .createdAt = 2, .other = 3, .key = 4 };
// const date = removeStructFields(user, &[_]u8{4});
// std.debug.print("\n{any}\n", .{date});
var gpa = std.testing.allocator_instance;
// _ = gpa;
var allocator = gpa.allocator();
// _ = allocator;
// const code = try generateRandomString(allocator);
const hash = try hashPassword(allocator, "password");
std.debug.print("\nGot {}\n", .{hash});
const user = .{ .id = 0, .createdAt = 2, .other = 3, .key = 4 };
const date = removeStructFields(user, &[_]u8{4});
std.debug.print("\n{any}\n", .{date});
}

View File

@ -1,10 +0,0 @@
[Unit]
Description=Backend Zerver for RLuv app
After=network.target
[Service]
ExecStart=/home/mob/zerver/zig-out/bin/zerver --db_path /home/mob/zerver/data.db
Restart=always
[Install]
WantedBy=multi-user.target